内容简介 · · · · · ·
《深入理解Java虚拟机:JVM高级特性与最佳实践》内容简介:作为一位Java程序员,你是否也曾经想深入理解Java虚拟机,但是却被它的复杂和深奥拒之门外?没关系,本书极尽化繁为简之妙,能带领你在轻松中领略Java虚拟机的奥秘。本书是近年来国内出版的唯一一本与Java虚拟机相关的专著,也是唯一一本同时从核心理论和实际运用这两个角度去探讨Java虚拟机的著作,不仅理论分析得透彻,而且书中包含的典型案例和最佳实践也极具现实指导意义。
全书共分为五大部分。第一部分从宏观的角度介绍了整个Java技术体系的过去、现在和未来,以及如何独立地编译一个OpenJDK7,这对理解后面的内容很有帮助。第二部分讲解了JVM的自动内存管理,包括虚拟机内存区域的划分原理以及各种内存溢出异常产生的原因;常见的垃圾收集算法以及垃圾收集器的特点和工作原理;常见的虚拟机的监控与调试工...
《深入理解Java虚拟机:JVM高级特性与最佳实践》内容简介:作为一位Java程序员,你是否也曾经想深入理解Java虚拟机,但是却被它的复杂和深奥拒之门外?没关系,本书极尽化繁为简之妙,能带领你在轻松中领略Java虚拟机的奥秘。本书是近年来国内出版的唯一一本与Java虚拟机相关的专著,也是唯一一本同时从核心理论和实际运用这两个角度去探讨Java虚拟机的著作,不仅理论分析得透彻,而且书中包含的典型案例和最佳实践也极具现实指导意义。
全书共分为五大部分。第一部分从宏观的角度介绍了整个Java技术体系的过去、现在和未来,以及如何独立地编译一个OpenJDK7,这对理解后面的内容很有帮助。第二部分讲解了JVM的自动内存管理,包括虚拟机内存区域的划分原理以及各种内存溢出异常产生的原因;常见的垃圾收集算法以及垃圾收集器的特点和工作原理;常见的虚拟机的监控与调试工具的原理和使用方法。第三部分分析了虚拟机的执行子系统,包括Class的文件结构以及如何存储和访问Class中的数据;虚拟机的类创建机制以及类加载器的工作原理和它对虚拟机的意义;虚拟机字节码的执行引擎以及它在实行代码时涉及的内存结构。第四部分讲解了程序的编译与代码的优化,阐述了泛型、自动装箱拆箱、条件编译等语法糖的原理;讲解了虚拟机的热点探测方法、HotSpot的即时编译器、编译触发条件,以及如何从虚拟机外部观察和分析JIT编译的数据和结果。第五部分探讨了Java实现高效并发的原理,包括JVM内存模型的结构和操作;原子性、可见性和有序性在Java内存模型中的体现;先行发生原则的规则和使用;线程在Java语言中的实现原理;虚拟机实现高效并发所做的一系列锁优化措施。
本书适合所有Java程序员、系统调优师和系统架构师阅读。
海报:
点击链接进入Java程序设计:
《深入理解Java虚拟机:JVM高级特性与最佳实践》
《JAVA核心技术(卷1):基础知识(原书第8版)》
《JAVA核心技术卷2:高级特征》
《Java语言程序设计(基础篇)(原书第8版)》
《Java编程思想(第4版)》
《Effective Java中文版(第2版)》
《Java编程思想(英文版•第4版)》
《Java 实时编程》
《Java加密与解密的艺术》
《Eclipse插件开发(原书第3版)(畅销插件开发指南新版)》
《Java语言程序设计:进阶篇(原书第8版)》
《Maven实战》
作者简介 · · · · · ·
资深Java技术专家,对JavaEE企业级应用开发、OSGi、Java虚拟机和工作流等都有深入的研究,并在大量的实践中积累了丰富的经验。尤其精通Java虚拟机,撰写了大量与JVM相关的经典文章,被各大技术社区争相转载,是ITeye等技术社区公认的Java虚拟机方面的领袖人物之一。现任远光软件股份有限公司平台开发部经理兼平台架构师,先后参加与过国家电网、南方电网等多个国家级大型ERP项目的平台架构工作,对软件系统架构也有深刻的认识和体会。
目录 · · · · · ·
致 谢
第一部分 走近Java
第1章 走近Java / 2
1.1 概述 / 2
1.2 Java技术体系 / 3
1.3 Java发展史 / 5
1.4 展望Java技术的未来 / 9
1.4.1 模块化 / 9
1.4.2 混合语言 / 9
1.4.3 多核并行 / 11
1.4.4 进一步丰富语法 / 12
1.4.5 64位虚拟机 / 13
1.5 实战:自己编译JDK / 13
1.5.1 获取JDK源码 / 13
1.5.2 系统需求 / 14
1.5.3 构建编译环境 / 15
1.5.4 准备依赖项 / 17
1.5.5 进行编译 / 18
1.6 本章小结 / 21
第二部分 自动内存管理机制
第2章 Java内存区域与内存溢出异常 / 24
2.1 概述 / 24
2.2 运行时数据区域 / 25
2.2.1 程序计数器 / 25
2.2.2 Java虚拟机栈 / 26
2.2.3 本地方法栈 / 27
2.2.4 Java堆 / 27
2.2.5 方法区 / 28
2.2.6 运行时常量池 / 29
2.2.7 直接内存 / 29
2.3 对象访问 / 30
2.4 实战:OutOfMemoryError异常 / 32
2.4.1 Java堆溢出 / 32
2.4.2 虚拟机栈和本地方法栈溢出 / 35
2.4.3 运行时常量池溢出 / 38
2.4.4 方法区溢出 / 39
2.4.5 本机直接内存溢出 / 41
2.5 本章小结 / 42
第3章 垃圾收集器与内存分配策略 / 43
3.1 概述 / 43
3.2 对象已死? / 44
3.2.1 引用计数算法 / 44
3.2.2 根搜索算法 / 46
3.2.3 再谈引用 / 47
3.2.4 生存还是死亡? / 48
3.2.5 回收方法区 / 50
3.3 垃圾收集算法 / 51
3.3.1 标记 -清除算法 / 51
3.3.2 复制算法 / 52
3.3.3 标记-整理算法 / 54
3.3.4 分代收集算法 / 54
3.4 垃圾收集器 / 55
3.4.1 Serial收集器 / 56
3.4.2 ParNew收集器 / 57
3.4.3 Parallel Scavenge收集器 / 59
3.4.4 Serial Old收集器 / 60
3.4.5 Parallel Old收集器 / 61
3.4.6 CMS收集器 / 61
3.4.7 G1收集器 / 64
3.4.8 垃圾收集器参数总结 / 64
3.5 内存分配与回收策略 / 65
3.5.1 对象优先在Eden分配 / 66
3.5.2 大对象直接进入老年代 / 68
3.5.3 长期存活的对象将进入老年代 / 69
3.5.4 动态对象年龄判定 / 71
3.5.5 空间分配担保 / 73
3.6 本章小结 / 75
第4章 虚拟机性能监控与故障处理工具 / 76
4.1 概述 / 76
4.2 JDK的命令行工具 / 76
4.2.1 jps:虚拟机进程状况工具 / 79
4.2.2 jstat:虚拟机统计信息监视工具 / 80
4.2.3 jinfo:Java配置信息工具 / 82
4.2.4 jmap:Java内存映像工具 / 82
4.2.5 jhat:虚拟机堆转储快照分析工具 / 84
4.2.6 jstack:Java堆栈跟踪工具 / 85
4.3 JDK的可视化工具 / 87
4.3.1 JConsole:Java监视与管理控制台 / 88
4.3.2 VisualVM:多合一故障处理工具 / 96
4.4 本章小结 / 105
第5章 调优案例分析与实战 / 106
5.1 概述 / 106
5.2 案例分析 / 106
5.2.1 高性能硬件上的程序部署策略 / 106
5.2.2 集群间同步导致的内存溢出 / 109
5.2.3 堆外内存导致的溢出错误 / 110
5.2.4 外部命令导致系统缓慢 / 112
5.2.5 服务器JVM进程崩溃 / 113
5.3 实战:Eclipse运行速度调优 / 114
5.3.1 调优前的程序运行状态 / 114
5.3.2 升级JDK 1.6的性能变化及兼容问题 / 117
5.3.3 编译时间和类加载时间的优化 / 122
5.3.4 调整内存设置控制垃圾收集频率 / 126
5.3.5 选择收集器降低延迟 / 130
5.4 本章小结 / 133
第三部分 虚拟机执行子系统
第6章 类文件结构 / 136
6.1 概述 / 136
6.2 无关性的基石 / 136
6.3 Class类文件的结构 / 138
6.3.1 魔数与Class文件的版本 / 139
6.3.2 常量池 / 141
6.3.3 访问标志 / 147
6.3.4 类索引、父类索引与接口索引集合 / 148
6.3.5 字段表集合 / 149
6.3.6 方法表集合 / 153
6.3.7 属性表集合 / 155
6.4 Class文件结构的发展 / 168
6.5 本章小结 / 170
第7章 虚拟机类加载机制 / 171
7.1 概述 / 171
7.2 类加载的时机 / 172
7.3 类加载的过程 / 176
7.3.1 加载 / 176
7.3.2 验证 / 178
7.3.3 准备 / 181
7.3.4 解析 / 182
7.3.5 初始化 / 186
7.4 类加载器 / 189
7.4.1 类与类加载器 / 189
7.4.2 双亲委派模型 / 191
7.4.3 破坏双亲委派模型 / 194
7.5 本章小结 / 197
第8章 虚拟机字节码执行引擎 / 198
8.1 概述 / 198
8.2 运行时栈帧结构 / 199
8.2.1 局部变量表 / 199
8.2.2 操作数栈 / 204
8.2.3 动态连接 / 206
8.2.4 方法返回地址 / 206
8.2.5 附加信息 / 207
8.3 方法调用 / 207
8.3.1 解析 / 207
8.3.2 分派 / 209
8.4 基于栈的字节码解释执行引擎 / 221
8.4.1 解释执行 / 221
8.4.2 基于栈的指令集与基于寄存器的指令集 / 223
8.4.3 基于栈的解释器执行过程 / 224
8.5 本章小结 / 230
第9章 类加载及执行子系统的案例与实战 / 231
9.1 概述 / 231
9.2 案例分析 / 231
9.2.1 Tomcat:正统的类加载器架构 / 232
9.2.2 OSGi:灵活的类加载器架构 / 235
9.2.3 字节码生成技术与动态代理的实现 / 238
9.2.4 Retrotranslator:跨越JDK版本 / 242
9.3 实战:自己动手实现远程执行功能 / 246
9.3.1 目标 / 246
9.3.2 思路 / 247
9.3.3 实现 / 248
9.3.4 验证 / 255
9.4 本章小结 / 256
第四部分 程序编译与代码优化
第10章 早期(编译期)优化 / 258
10.1 概述 / 258
10.2 Javac编译器 / 259
10.2.1 Javac的源码与调试 / 259
10.2.2 解析与填充符号表 / 262
10.2.3 注解处理器 / 264
10.2.4 语义分析与字节码生成 / 264
10.3 Java语法糖的味道 / 268
10.3.1 泛型与类型擦除 / 268
10.3.2 自动装箱、拆箱与遍历循环 / 273
10.3.3 条件编译 / 275
10.4 实战:插入式注解处理器 / 276
10.4.1 实战目标 / 276
10.4.2 代码实现 / 277
10.4.3 运行与测试 / 284
10.4.4 其他应用案例 / 286
10.5 本章小结 / 286
第11章 晚期(运行期)优化 / 287
11.1 概述 / 287
11.2 HotSpot虚拟机内的即时编译器 / 288
11.2.1 解释器与编译器 / 288
11.2.2 编译对象与触发条件 / 291
11.2.3 编译过程 / 294
11.2.4 查看与分析即时编译结果 / 297
11.3 编译优化技术 / 301
11.3.1 优化技术概览 / 301
11.3.2 公共子表达式消除 / 305
11.3.3 数组边界检查消除 / 307
11.3.4 方法内联 / 307
11.3.5 逃逸分析 / 309
11.4 Java与C/C++的编译器对比 / 311
11.5 本章小结 / 313
第五部分 高效并发
第12章 Java内存模型与线程 / 316
12.1 概述 / 316
12.2 硬件的效率与一致性 / 317
12.3 Java内存模型 / 318
12.3.1 主内存与工作内存 / 319
12.3.2 内存间交互操作 / 320
12.3.3 对于volatile型变量的特殊规则 / 322
12.3.4 对于long和double型变量的特殊规则 / 327
12.3.5 原子性、可见性与有序性 / 328
12.3.6 先行发生原则 / 330
12.4 Java与线程 / 333
12.4.1 线程的实现 / 333
12.4.2 Java线程调度 / 337
12.4.3 状态转换 / 339
12.5 本章小结 / 341
第13章 线程安全与锁优化 / 342
13.1 概述 / 342
13.2 线程安全 / 343
13.2.1 Java语言中的线程安全 / 343
13.2.2 线程安全的实现方法 / 348
13.3 锁优化 / 356
13.3.1 自旋锁与自适应自旋 / 356
13.3.2 锁消除 / 357
13.3.3 锁粗化 / 358
13.3.4 轻量级锁 / 358
13.3.5 偏向锁 / 361
13.4 本章小结 / 362
附录A Java虚拟机家族 / 363
附录B 虚拟机字节码指令表 / 366
附录C HotSpot虚拟机主要参数表 / 372
附录D 对象查询语言(OQL)简介 / 376
附录E JDK历史版本轨迹 / 383
· · · · · · (收起)
原文摘录 · · · · · · ( 全部 )
-
GC日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,而不是 用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The-World 的 (查看原文) —— 引自第89页 -
由于使用了准确式内存管理,Exact VM可以抛弃掉以前Classic VM基于handler的对象查找方式(原因是GC后对象将可能会被移动位置,如果地址为123456的对象移动到654321,在没有明确信息表明内存中哪些数据是reference的前提下,那虚拟机是不敢把内存中所有为123456的值改成654321的,所以要使用句柄来保持reference值的稳定),这样每次定位对象都少了一次间接查找的开销,提升执行性能。 (查看原文) —— 引自第10页
> 全部原文摘录
丛书信息
喜欢读"深入理解Java虚拟机"的人也喜欢的电子书 · · · · · ·
喜欢读"深入理解Java虚拟机"的人也喜欢 · · · · · ·
深入理解Java虚拟机的话题 · · · · · · ( 全部 条 )



深入理解Java虚拟机的书评 · · · · · · ( 全部 75 条 )

Java虚拟机一本通

JVM从入门到放弃?不要这么轻易就放弃啊

深入理解Java虚拟机:JVM高级特性与最佳实践 读后感

深入理解Java虚拟机(第三版)书评

Java虚拟机,有这本就够了
> 更多书评 75篇
-
-
1. 关于finally 和return finall保证无论发生什么情况都能执行的原因是,在字节码中,每个执行路径上进行了冗余 如果方法return a=1 但是finally里修改了a=2,最终返回值还是1,原因是return指令会把返回值放到局部变量表中,而且不是a所对应的那个局部变量,后续冗余代码只能操作a,不能操作returnvalue,所以,返回1 栈帧包含指令栈、局部变量表、动态链接、返回地址 几个常用指令 load 和const 都是入栈,一个将局部变量表入...
2018-12-24 00:10 1人喜欢
1. 关于finally 和return
finall保证无论发生什么情况都能执行的原因是,在字节码中,每个执行路径上进行了冗余
如果方法return a=1 但是finally里修改了a=2,最终返回值还是1,原因是return指令会把返回值放到局部变量表中,而且不是a所对应的那个局部变量,后续冗余代码只能操作a,不能操作returnvalue,所以,返回1
栈帧包含指令栈、局部变量表、动态链接、返回地址
几个常用指令 load 和const 都是入栈,一个将局部变量表入栈,一个将常量入栈 store 将栈顶值赋值给local
2.静态分派和动态分派 重载属于静态分派 重写属于动态分派invokevirtul 执行虚方法(当抽象类或者接口引用调用方法的时候,会被编译成这个指令) 静态多分派动态单分派
3.动态类型
reflection和methodhandle reflection只是为java设计,methodhandle模拟invokerdynamic 可以支持其他语言
4.范型是通过擦除实现的
5.运行期优化举例:公共子表达式消除、无用代码消除、方法内联、复写聚合、逃逸分析(可以直接把对象分配到栈上,可以不用垃圾回收)
回应 2018-12-24 00:10 -
第二章 java内存区域与内存溢出异常 有两个误解: 1.基本类型数据都保存在栈上,是错误的。栈中存储的是栈帧,当一个方法执行的时候,方法中声明的基本类型变量直接在栈上分配,对象在堆上分配。但是如果是对象的属性是基本类型,或者,方法中声明基本类型的数组,还是会在堆上分配 2.方法区有个常量池,1.6以前,常量放在方法区,1.7常量放在堆中,方法区只保留常量的引用,1.8中移除了常量区,改为了本地内存实现的元数据区 ...
2018-12-23 11:59 1人喜欢
第二章 java内存区域与内存溢出异常
有两个误解:
1.基本类型数据都保存在栈上,是错误的。栈中存储的是栈帧,当一个方法执行的时候,方法中声明的基本类型变量直接在栈上分配,对象在堆上分配。但是如果是对象的属性是基本类型,或者,方法中声明基本类型的数组,还是会在堆上分配
2.方法区有个常量池,1.6以前,常量放在方法区,1.7常量放在堆中,方法区只保留常量的引用,1.8中移除了常量区,改为了本地内存实现的元数据区
第三章 垃圾回收与内存分配策略
疑问1:gc日志中的gc 和fullgc 有的说法说fullgc是老年代的gc 有的说法是fullgc是发生了stop the world 的gc 两个说法都不合理,正确的判断方法是查看日志,而且fullgc 时间长不是说fullgc stw的时间长,而是整个gc花费的时间长。
gc 日志相关参如下:
-XX:+PrintGC 打印简单日志
-XX:PrintGCDetails 打印详细日志
如果只看简单日志中的 fullgc 很难判断到底是什么意思,所以需要看详细日志中的信息
比如:下面这个说明这次full gc 回收了 堆和方法区
[Full GC
[PSYoungGen: 10752K->9707K(142848K)]
[ParOldGen: 232384K->232244K(485888K)] 243136K->241951K(628736K)
[PSPermGen: 3162K->3161K(21504K)], 1.5265450 secs
]
简单的理解 fullgc 耗时比minorgc 长的原因是 minor 一般采用标记复制的方式,每次复制的都比较少,所以会比较快,但是收集的过程中也需要stop the world, fullgc 不仅回收年轻代 还会回收老年代,老年代存活的对象有可能比较多,所以标记删除要慢一些,并不是只有老年代回收才会stw,如果老年代使用cms算法,老年代回收stw 的时间还会比serial之类的回收器要少 但是由于日志统计的不是stw的时间 而是整个gc的时间 所以fullgc 时间比较长。
2.-XX:ParallelGCThreads 不只是针对parallal 垃圾收集器的设置 向parnew cms 这种有多线程收集的都会起作用
3,parallal 收集器可以配置-XX:UseAdaptiveSizePolicy不用指定堆内部各个部分的大小细节,相当于直接告诉jvm需要优化的目标,让jvm自己处理优化逻辑。
4.cms 因为有浮动垃圾的存在,不能等老年代满了才进行垃圾回收,所以参数-XX:CMSInitiaingOccupancyFraction 可以设置触发的百分比,增大该值会减少老年代内存回收次数,但是太大会导致发生concurrent mode failure 的概率,会触发serial old 收集 相当于要进行两次收集 性能反而降低
5.cms 每次fullgc 会压缩内存清理碎片,可以配置多少次fullgc 以后执行一次压缩 默认是每次fullgc都压缩
6.g1收集器,把内存划分为region 并对region进行排序,按照回收价值顺序清理每个region的垃圾,不会有碎片,因为不用每次清理整个老年代或者年轻代,所以可以精确的设置 在xxx时间范围内 垃圾回收时间不得超过xxx时间,每个region 维护一个rememberset 标记自己引用的其他region的对象,才实现了不需要扫描整个空间就能确定哪些是垃圾对象。
7.与内存管理相关的jvm配置项
1.与垃圾收集器无关的:
-Xms -Xmx 堆的大小,如果两个值不相等,可以配置当内存使用大于xxx的时候扩容 小于xxx的时候缩容
-Xmn -XX:NewSizw -XX:MaxNewSize 第一个相当于设置后两个相等,老年代的大小等于总内存减去年轻代
-XX:NewRatio 老年代跟新生代的比值 设置为4 新生代占1/5 如果同时设置绝对值与百分比 以绝对值为准 ‘默认2’
-XX:SurvivorRatio eden 和 survivor的比值 默认是8
-XX:PretenureSizeThreshold 超过这个大小的对象会直接分配到老年代
-XX:MaxTenuringThreashold 超过这个年龄的对象会被复制到老年代
-XX:HandlePromotionFailure 如果老年代剩余连续空间能存放年轻代的所有对象,则这次回收是安全的,如果不允许担保失败,这个时候会进行fullgc
2.与垃圾收集器相关的参数
parallel 可以开启自适应模式,并设置gc时间比率和gc最大停顿时间
cms 可以设置老年代收集的阈值,和fullgc多少次执行一次内存整理
8.jvm调优
jvm内存管理相关有xxx(上边第7点)首先根据服务类型 追求吞吐量的服务和追求响应时间的服务,选择合适的垃圾收集算法,然后 分析代码确认内存中对象的大小分布和生命周期情况
java代码运行时候肯定会生成很多对象,所以垃圾回收是不可避免的,如果内存某个区域分配很大,那么会减少gc的次数但是会增大每次gc的时间,相反如果内存很小,每次gc时间很短,就会增加gc次数,所以调优就是找到一个平衡点。
为了方便调优,一般设置堆的初始大小和最大大小相等,防止堆扩容缩容对性能的影响
更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
如何选择应该依赖应用程序对象生命周期的分布情况:
如果应用存在大量的临时对象比如web服务,应该选择更大的年轻代;
如果存在相对较多的持久对象,比如缓存服务,年老代应该适当增大。
如果应用没有明显的特征,就需要不停的修改参数测试直到找到最合适的大小。
回应 2018-12-23 11:59 -
Thinker Wu (技术就是信仰!志在终身探索!)
-
Java从诞生到现在已有10余年时间,开发者竟有千万之多,那it从业人员岂不是海了去了。随便给这些人搞点东西都是个很大的市场啊。还有,跟上千万人抢饭碗压力好大。 自己稀里糊涂搞起了Java,看一下众多特性,发现还是有不少自己感兴趣的地方。现在的学习完全流于表面,感觉离技术这一行有点越来越远。
2011-10-07 20:04
-
1. 关于finally 和return finall保证无论发生什么情况都能执行的原因是,在字节码中,每个执行路径上进行了冗余 如果方法return a=1 但是finally里修改了a=2,最终返回值还是1,原因是return指令会把返回值放到局部变量表中,而且不是a所对应的那个局部变量,后续冗余代码只能操作a,不能操作returnvalue,所以,返回1 栈帧包含指令栈、局部变量表、动态链接、返回地址 几个常用指令 load 和const 都是入栈,一个将局部变量表入...
2018-12-24 00:10 1人喜欢
1. 关于finally 和return
finall保证无论发生什么情况都能执行的原因是,在字节码中,每个执行路径上进行了冗余
如果方法return a=1 但是finally里修改了a=2,最终返回值还是1,原因是return指令会把返回值放到局部变量表中,而且不是a所对应的那个局部变量,后续冗余代码只能操作a,不能操作returnvalue,所以,返回1
栈帧包含指令栈、局部变量表、动态链接、返回地址
几个常用指令 load 和const 都是入栈,一个将局部变量表入栈,一个将常量入栈 store 将栈顶值赋值给local
2.静态分派和动态分派 重载属于静态分派 重写属于动态分派invokevirtul 执行虚方法(当抽象类或者接口引用调用方法的时候,会被编译成这个指令) 静态多分派动态单分派
3.动态类型
reflection和methodhandle reflection只是为java设计,methodhandle模拟invokerdynamic 可以支持其他语言
4.范型是通过擦除实现的
5.运行期优化举例:公共子表达式消除、无用代码消除、方法内联、复写聚合、逃逸分析(可以直接把对象分配到栈上,可以不用垃圾回收)
回应 2018-12-24 00:10 -
第二章 java内存区域与内存溢出异常 有两个误解: 1.基本类型数据都保存在栈上,是错误的。栈中存储的是栈帧,当一个方法执行的时候,方法中声明的基本类型变量直接在栈上分配,对象在堆上分配。但是如果是对象的属性是基本类型,或者,方法中声明基本类型的数组,还是会在堆上分配 2.方法区有个常量池,1.6以前,常量放在方法区,1.7常量放在堆中,方法区只保留常量的引用,1.8中移除了常量区,改为了本地内存实现的元数据区 ...
2018-12-23 11:59 1人喜欢
第二章 java内存区域与内存溢出异常
有两个误解:
1.基本类型数据都保存在栈上,是错误的。栈中存储的是栈帧,当一个方法执行的时候,方法中声明的基本类型变量直接在栈上分配,对象在堆上分配。但是如果是对象的属性是基本类型,或者,方法中声明基本类型的数组,还是会在堆上分配
2.方法区有个常量池,1.6以前,常量放在方法区,1.7常量放在堆中,方法区只保留常量的引用,1.8中移除了常量区,改为了本地内存实现的元数据区
第三章 垃圾回收与内存分配策略
疑问1:gc日志中的gc 和fullgc 有的说法说fullgc是老年代的gc 有的说法是fullgc是发生了stop the world 的gc 两个说法都不合理,正确的判断方法是查看日志,而且fullgc 时间长不是说fullgc stw的时间长,而是整个gc花费的时间长。
gc 日志相关参如下:
-XX:+PrintGC 打印简单日志
-XX:PrintGCDetails 打印详细日志
如果只看简单日志中的 fullgc 很难判断到底是什么意思,所以需要看详细日志中的信息
比如:下面这个说明这次full gc 回收了 堆和方法区
[Full GC
[PSYoungGen: 10752K->9707K(142848K)]
[ParOldGen: 232384K->232244K(485888K)] 243136K->241951K(628736K)
[PSPermGen: 3162K->3161K(21504K)], 1.5265450 secs
]
简单的理解 fullgc 耗时比minorgc 长的原因是 minor 一般采用标记复制的方式,每次复制的都比较少,所以会比较快,但是收集的过程中也需要stop the world, fullgc 不仅回收年轻代 还会回收老年代,老年代存活的对象有可能比较多,所以标记删除要慢一些,并不是只有老年代回收才会stw,如果老年代使用cms算法,老年代回收stw 的时间还会比serial之类的回收器要少 但是由于日志统计的不是stw的时间 而是整个gc的时间 所以fullgc 时间比较长。
2.-XX:ParallelGCThreads 不只是针对parallal 垃圾收集器的设置 向parnew cms 这种有多线程收集的都会起作用
3,parallal 收集器可以配置-XX:UseAdaptiveSizePolicy不用指定堆内部各个部分的大小细节,相当于直接告诉jvm需要优化的目标,让jvm自己处理优化逻辑。
4.cms 因为有浮动垃圾的存在,不能等老年代满了才进行垃圾回收,所以参数-XX:CMSInitiaingOccupancyFraction 可以设置触发的百分比,增大该值会减少老年代内存回收次数,但是太大会导致发生concurrent mode failure 的概率,会触发serial old 收集 相当于要进行两次收集 性能反而降低
5.cms 每次fullgc 会压缩内存清理碎片,可以配置多少次fullgc 以后执行一次压缩 默认是每次fullgc都压缩
6.g1收集器,把内存划分为region 并对region进行排序,按照回收价值顺序清理每个region的垃圾,不会有碎片,因为不用每次清理整个老年代或者年轻代,所以可以精确的设置 在xxx时间范围内 垃圾回收时间不得超过xxx时间,每个region 维护一个rememberset 标记自己引用的其他region的对象,才实现了不需要扫描整个空间就能确定哪些是垃圾对象。
7.与内存管理相关的jvm配置项
1.与垃圾收集器无关的:
-Xms -Xmx 堆的大小,如果两个值不相等,可以配置当内存使用大于xxx的时候扩容 小于xxx的时候缩容
-Xmn -XX:NewSizw -XX:MaxNewSize 第一个相当于设置后两个相等,老年代的大小等于总内存减去年轻代
-XX:NewRatio 老年代跟新生代的比值 设置为4 新生代占1/5 如果同时设置绝对值与百分比 以绝对值为准 ‘默认2’
-XX:SurvivorRatio eden 和 survivor的比值 默认是8
-XX:PretenureSizeThreshold 超过这个大小的对象会直接分配到老年代
-XX:MaxTenuringThreashold 超过这个年龄的对象会被复制到老年代
-XX:HandlePromotionFailure 如果老年代剩余连续空间能存放年轻代的所有对象,则这次回收是安全的,如果不允许担保失败,这个时候会进行fullgc
2.与垃圾收集器相关的参数
parallel 可以开启自适应模式,并设置gc时间比率和gc最大停顿时间
cms 可以设置老年代收集的阈值,和fullgc多少次执行一次内存整理
8.jvm调优
jvm内存管理相关有xxx(上边第7点)首先根据服务类型 追求吞吐量的服务和追求响应时间的服务,选择合适的垃圾收集算法,然后 分析代码确认内存中对象的大小分布和生命周期情况
java代码运行时候肯定会生成很多对象,所以垃圾回收是不可避免的,如果内存某个区域分配很大,那么会减少gc的次数但是会增大每次gc的时间,相反如果内存很小,每次gc时间很短,就会增加gc次数,所以调优就是找到一个平衡点。
为了方便调优,一般设置堆的初始大小和最大大小相等,防止堆扩容缩容对性能的影响
更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
如何选择应该依赖应用程序对象生命周期的分布情况:
如果应用存在大量的临时对象比如web服务,应该选择更大的年轻代;
如果存在相对较多的持久对象,比如缓存服务,年老代应该适当增大。
如果应用没有明显的特征,就需要不停的修改参数测试直到找到最合适的大小。
回应 2018-12-23 11:59 -
Thinker Wu (技术就是信仰!志在终身探索!)
-
Thinker Wu (技术就是信仰!志在终身探索!)
论坛 · · · · · ·
长期招聘JVM大牛 | 来自飞逝的梦想 | 2015-01-08 | |
代码清单2-5 借助CGLib使得方法区出现内存溢出异常... | 来自千里驴 | 2 回应 | 2012-08-11 |
求指教,67页说明“对象优先在Eden分配”的疑惑 | 来自whg | 2012-08-11 | |
不错 | 来自jackQ | 2011-07-05 |
这本书的其他版本 · · · · · · ( 全部3 )
-
机械工业出版社 (2013)9.0分 1809人读过
-
机械工业出版社 (2019)9.5分 328人读过
-
HHKNT3(满200-30)ZVYBKQ(满300-60)
以下书单推荐 · · · · · · ( 全部 )
谁读这本书?
二手市场
订阅关于深入理解Java虚拟机的评论:
feed: rss 2.0
0 有用 init.d 2013-07-15
不明觉厉
0 有用 俊 2011-10-07
看了前面三章!
0 有用 R2D2 2012-12-11
2012.9.26-12.11 笔记:http://liulixiang.info/wiki/index.php?title=%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E8%99%9A%E6%8B%9F%E6%9C%BA
0 有用 仙人球 2012-11-20
关键的部分有,但是零散。
0 有用 hoterran 2013-07-01
八卦,技术完美的结合。可惜好几章都没看懂。要再读。
0 有用 预见未来to50 2020-10-21
囫囵吞枣的读了一遍
0 有用 Joke 2020-07-05
国人计算机书籍中少有的经典,深入讲解JVM原理,帮助我们理解Java运行的底层机制,有助于我们在开发实践中进行调优、问题排查等。
0 有用 Lightman 2020-03-07
现在有第三版了
0 有用 冬天艳阳 2020-01-30
对于一个java技术入门者来说,收获很多,但又觉得无处使用。不知道这是不是我一个人的困惑。整本书涉及到的内容非常多,通过查阅资料,反复看了两遍,仍觉得没有形成系统的认知,那就继续看吧!
0 有用 happy15 2019-12-25
了解并发机制挺好的