ref:http://www.cnblogs.com/ityouknow/p/6482464.html
注1:看了大神:纯洁的微笑的JVM系列篇,发现好多地方还是似懂非懂,理解的并不透彻,jvm的调优部分更是稀里糊涂;
本片主要整理下jvm部分的知识点,方便以后的面试使用,(权且死记硬背了 !>_<!)
注2:由于不同的JVM厂商不同,关于JVM的分区,存在几个不确定的地方,以下简述对这些不甚明了的地方的个人理解,难免有误:
1、关于JVM的内存划分,“方法区”的叫法是JVM的规范中的术语,而Oracle公司的HotSpot虚拟机对方法区的实现是永久代(PerGen space),所以只有HotSpot才有永久带,也就有了对永久带的配置参数-XX:PermSize,网上的JVM的分析大多对于HotSpot的,但要明白“方法区”与“PerGen space”两者不是等价的。
2、JDK8中废弃了永久带(PermGen),启用了元空间(Metaspace)
ref:https://www.cnblogs.com/yulei126/p/6777323.html
ref:http://www.cnblogs.com/paddix/p/5309550.html
废弃原因:
PermGen现实中经常爆OutOfMemoryError异常,hotspot中有参数实现PerGen空间的配置
oracle公司融合自身HotSpot JVM与 JRockit VM两种虚拟机,后者没有永久带,不需要配置;
(永久代会为 GC 带来不必要的复杂度,并且回收效率偏低)
元空间Metaspace也是对方法区的实现,作用同以前的PermGen,其最大不同在于:Metaspace不在JVM中,而是使用本地内存(即JVM以外的本机内存)存放。因此,默认情况下Metaspace只收到本机内存的限制,同样也可以通过参数指定如:-XX:MetaspaceSize等。__
JDK1.7中,就已经将PermGen中的部分数据转移到其他地方存储:如类的静态变量转移到Heap区,字符串也在Heap区???
<---------------------------------------------------------------------正片开始----------------------------------------------->
类的加载机制
JVM的内存结构
GC算法 垃圾回收
GC分析 命令调优
注:底部给出思维导图参考
1.1:什么是类的加载:
(1)将类的.class文件以二进制读入到内存,放入运行时数据区的方法区,然后在堆区创建一个java.lang.Class对象,以封装类在方法区的数据结构。
(2)类加载的最终产品是位于堆区的Class对象,该Class对象封装类在方法区的数据结构,并向JAVA程序员提供了访问方法区内数据解耦的接口。
1.2:类的生命周期:
####: 类的生命周期:
#---- 类的加载
|---- 装载(Loading)(为了区分加载,这里称其为装载):查找并导入类的Class文件,在堆区创建java.lang.Class的对象。
*|---- 连接*过程分为三块:
|---- 验证:文件的格式、元数据、字节码、符号引用验证,确保被装载的类的正确性,该阶段虽然重要但不是必须的;
|---- 准备:为类的静态变量(static)分配内存,并将其初始化为默认值;如 public static int value=3 ,此时value的值是0而不是3;而对于: public static final int value=3 ,该阶段value的值是3,而不是0,可以认为final static 常量在javac的编译期间,为value生成了ConstantValue属性,准备阶段jvm根据ConstantValue的值为value指定值。
|---- 解析:将符号引用转换为直接引用;
|---- 初始化:为类的静态变量,静态代码块等赋予正确的初始值。
#---- 类的使用:new出对象供程序使用。
#---- 类的卸载:执行垃圾回收。
补充问题:
1.2.1:JVM的初始化步骤(初始化过程):
1.2.2:类的初始化时机:
1.2.3:哪几种情况下,JVM会结束生命周期:
1、执行了system.exit();
2、程序正常结束;
3、执行过程中发生异常或错误而终止;
4、操作系统的错误导致JVM的进程终止;
启动类加载器:Bootstrap ClassLoader,负责加载JDK\jre\lib目录下或被-Xbootclasspath指定位置处,可悲jvm识别的类库;
扩展类加载器:Extension ClassLoader,负责加载JDK\jre\lib\ext目录下或由java.ext.dirs系统变量所指定路径中的所有类库;开发者可以直接使用该加载器;
应用程序加载器:ApplicationClassLoader,负责加载用户类路径(Classpath)所指定的类,开发者可以直接使用该类加载器;
类的加载机制:
全盘负责:某个类加载器负责加载某个Class,对该Class所依赖和引用的其他Class都由该加载器负责加载,除非显示指定。
父类委托(双亲委托机制):某个类加载器收到Class加载请求时,现将该请求转至父类加载器,对Class进行加载,父类找不到该类无法完成加载,才尝试自己加载。
缓存机制:所有加载过的Class都会被缓存,当程序中需要使用某个类时,类加载器先从缓存区寻找该Class,不存在,才会读取Class的二进制,生成Class对象存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。
2、JVM内存结构
方法区和堆是所有线程共享的内存区域;而java栈、本地方法栈和程序计数器是运行是线程私有的内存区域。
堆Heap:存放实例对象,是JVM内存中最大的一块,也是GC的主要区域。
* 年轻代(8:1:1)
* Eden空间
* From Survivor空间
* To Survivor空间
方法区Method Area:存放被jvm加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
程序计数器(Program Counter Register):是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
栈:
对象分配规则:
注:相关Minor GC、Major GC&Full GC参见:
ref1:http://www.importnew.com/15820.html :这是翻译的外国人的总结,感觉有些生硬;
ref2 : http://www.cnblogs.com/hnrainll/p/3410042.html :来自博客园,解释的很好,简单明了;
说明:
对象优先分配在Eden区,Eden区是连续的内存空间,如果Eden去没有足够的空间,JVM进行一次minor GC或Young GC(即从年轻代回收内存),将剩余的对象复制到一个Survivor区;
大对象直接进入老年代(需要大量连续内存空间的对象),以避免在Eden和两个survivor区间的大量内存拷贝。(新生代采用复制算法收集内存)
长期存活的对象进入老年代。JVM为每一个对象定义了一个年龄计数器,如果经过1次minor GC后,则进入survivor区,之后每一次Minor GC,该对象的年龄加1,直到达到阈值进老年区。
动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,那年龄大于或等于该年龄的对象进入老年区。
空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区剩余值的大小,则进行一次Full GC;如果小于检查HandlePromotionFailure设置,如果出现true则只进行Monitor GC,如果false,则进行Full GC。
注:另外网上还有另一种回答:
Survivor切换:每次的Minor GC都会将剩余对象添加到该Survivor区,当该Survivor满了以后,将其中活着的复制进另一个Survivor区,以后的Minor GC将或者的对象添加到切换后的Survivor区,两个Survivor总有一个是空的。
进入老年代:当在两个Survivor间切换了几次(HotSpot默认15次),将其中仍然存活的对象,复制到老年代。
Full GC:老年代空间一般较大,当老年代内存不足时,出发Major GC.(Full GC与Major GC的区别网上不清楚,参照ref1)
各个内存区域的控制参数大小:
-Xms 设置堆的最小空间;
-Xmx设置堆的最大空间大小;
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代(方法区)最小空间大小。(JDK1.8废弃PermGen)
-XX:MaxPermSize设置永久代(方法区)最大空间大小
-Xss设置每个线程的堆栈大小。
老年代的空间设置:Xms(Xmx)-XX:NewSize(MaxNewSize)进行设置;
3、GC算法 垃圾回收:
* 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,释放时减1,为0时回收。但无法解决对象相互循环引用的问题;
可达性分析:从GC roots开始向下搜索,搜索走过的路径称为引用链。当一个对象到GC roots没有任何引用链相连时,证明此对象是不可用的,不可达对象。
3.2、GC 算法:
* 标记-清除算法(Mark-Sweep) :第一阶段标记处所有需要回收的对象,第二阶段统一回收所有被标记的对象。
复制算法(Copying) :将可用内存按容量大小划分为相等的两块,每次只使用一块。当前块用完了,则将还活着的对象复制到另外一块上,并把当前块内存清空。
标记-压缩算法 :标记过程同Mark-Sweep算法,标记完成后,让所有存活的对象向一端移动,清理掉端边界以外的内存。
分代收集(Generational Collection):将JAVA堆分为年轻代和老年代,分别采用不同的收集算法(年轻代:复制;老年代标记整理)。
3.3、GC回收器
垃圾回收器是对GC算法的实现:
Serial收集器:是最古老、最稳定、效率高的收集器。参数控制:-XX:+UseSerialGC
ParNew收集器:是Serial收集器的多线程版本;参数控制:-XX:+UseParNewGC ParNew收集器 -XX:ParallelGCThreads 限制线程数量。
Parallel收集器:类似于ParNew,更关注系统的吞吐量(用户代码运行时间/总运行时间)
Parallel Old收集器:参数控制: -XX:+UseParallelOldGC 使用Parallel收集器+ 老年代并行
CMS收集器:以获取最短的回收停顿时间为目标。
优点:并发收集、低停顿。
缺点:大量空间碎片,并发降低吞吐量。
步骤:
4、GC分析 命令调优
JVM调优就是根据GC的日志分析JVM的内存分配、回收的情况来调整各区域的内存比例或GC回收的策略。???
4.1、GC日志分析
young gc日志格式:
full gc日志格式:
4.2、JVM调优命令
运用jvm自带的命令,可以方便的在生产监控和打印堆栈日志信息中帮助开发者定位问题。
Sun的JDK监控和故障处理命令主要有以下:
4.1.1:jps(JVM process status tool)显示指定系统内所有HotSpot的进程。格式:
jps [options] [hostid]
- l :输出主类全名或jar路径;
-q :只输出LVMID;
-m:输出jvm启动时传递给main()的参数;
- v:输出jvm启动时显示指定的jvm启动参数;
4.1.2:jstat(JVM statistics Monitoring)用于监视虚拟机运行时状态信息的命令,可以显示虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据;
jstat [option] LVMID [interval] [count] : 操作参数 本地虚拟机进程ID 连续输出时间间隔 连续输出的次数
jstat -gc 1262 2000 20 /*每隔2000ms输出1262的gc情况,输出20次
-class : 显示ClassLoader的行为统计;
-compiler:输出JIT编译过的方法数量耗时;
-gc :垃圾回收堆的行为统计;
-gccapacity:输出堆的各区域的最大最小空间;
-gcutil :输出堆的各区域已用空间占总空间的百分比;
-gccause :垃圾收集统计概述;
-gcnew :统计新生代行为;
-gcnewcapacity:统计新生代的内存空间;
-gcold :统计旧生代行为;
-gcoldcapacity :统计旧生代的大小和空间;
-gcpermcapacity:永生代的行为统计;
printcompilation :hotspot编译方法统计;
4.1.3:jmap(jvm memory map)用于生成heap dump文件(进程所使用内存情况的一次快照)。
jmap [option] LVMID
4.1.4:jhat(JVM heap analysis tool)与jmap搭配,分析jmap生成的dump;
4.1.5:jstack :jstack用于生成java虚拟机当前时刻的线程快照;
4.1.6:jinfo:实时查看和调整虚拟机的运行参数。
4.3、调优工具:
常用调优工具分为两类,
jdk自带监控工具:jconsole和jvisualvm(jdk/bin目录下)
第三方有:MAT(Memory Analyzer Tool)、GChisto。
<---------------------------------------------------------------------------------------------------------->
手机扫一扫
移动阅读更方便
你可能感兴趣的文章