JVM学习笔记一

342 阅读6分钟

1.JVM内存结构

总体来讲分为: 堆、JVM栈、本地方法栈、方法区、程序计数器。

堆区:
对象和数组存储在堆中,当前应用所有线程共享。 堆内存是JVM内存中最大的一块,也是垃圾回收的主要区域,又被称为”GC”堆,由年轻代与老年代构成。年轻代内存又被分为三部分:Eden空间、From Survivor空间、To Survivor空间。默认情况下年轻代按照8:1:1分配。

栈区:
方法入栈,每个方法执行时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口。局部变量表存储编译期可知的八种基本数据类型和对象引用。当进入一个方法时,这个方法在栈帧中给局部变量表分配多大的内存是完全确定的,在方法执行时其局部变量表内存不会改变。操作数栈存储用于计算的临时存储区域。
线程私有、后进先出、一个方法对应一个栈帧

本地方法栈: 虚拟机执行本地用C实现的功能

程序计数器: 通俗的讲是行号指示器。假如A方法内调用B方法,执行到一半线程被挂起了,下次接着执行就是靠程序计数器知道执行到哪里了,如果正在执行的是native方法,计数器值为空。它是JVM内存中唯一不会出现内存溢出的区域。

方法区: 存储常量、类元信息、static修饰的部分、线程共享。

学会使用JVM指令手册去分析Java字节码文件
手册地址:链接: note.youdao.com/noteshare?i…
使用IDEA查看java字节码文件: www.jianshu.com/p/4c8c4178d…

2.对象存活判断

引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,释放减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
可达性分析:从GCRoot根向下搜索,搜索所走过的路径称为引用链,不在这条链上的就是不可达对象(被判刑为垃圾对象,但还没死)

在Java语言中,GC Roots包括:
虚拟机栈中引用的对象、方法区中类静态属性实体引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象

3.JVM垃圾收集算法

3.1:分代收集:
目前来收集算法基本都采用分代收集的思想根据对象的存活周期划分内存区域并采用合适的算法。Java堆分为新生代和老年代,两者所占内存大小比例为1 :2。

新生代:(停止-复制算法)

新生代内存分为3个区域Eden区,Survivor0区,Survivor1区。内存大小比例为8:1:1。

  1. 对象创建时首先被分配到Eden区(大对象直接进入老年代),
  2. 当Eden区满了后,将触发一次MinorGC(youngGC),收集无引用的对象,并将剩余的对象复制到一个存活区Survivor0区去(两个Survivor总有一个为空),
  3. 此后重复上述行为,多次MinorGC后直到Survivor0区满了,在将存活的对象复制到Survivor1区
  4. 当两个存活区切换了几次后仍然存活的对象将进入老年代。(每一次MinorGC后存活的对象分代年龄会+1,分代年龄大于等于15的将被复制到老年代) 以上过程就是停止-复制清理法(将Eden区和Survivor区中存活的对象复制到另一个Survivor区) 。


老年代:(标记-整理算法)

存储大对象(很长的字符串或大数组)与老不死的对象(多次minorGC后还不被回收,分代年龄到了15后)。老年代空间较大,存储对象多,而且不乏大对象。如果使用停止-复制算法,则较为低效。它使用的是标记-整理算法,让所有存活的对象往一端移动,然后清理掉边界以外的对象。当老年代内存不足时,将触发Major GC,也叫Full GC。Full GC是针对新生代,老年代,元空间的一次全局GC。调优时候尤其要争对FullGC调,因为每次GC都会导致其他线程挂起,GC时间越长,用户体验就越差。

至于为什么是15次,原因是HotSpot会在对象头的中的标记字段里记录年龄,分配到的空间只有4位,所以最多只能记录到15

参数:
-XX:PretenureSizeThreshold:控制直接升入老年代的对象大小,大于这个值的对象会直接分配在老年代上。
-XX:+UseAdaptiveSizePolicy:控制是否采用动态控制策略,如果动态控制,则动态调整Java堆中各个区域的大小以及进入老年代的年龄


3.2:标记-清除算法: 算法分为“标记”与“清除”两个阶段。首先标记出所有需要回收的对象(可达性分析),在标记完成后统一清理掉所有被标记的对象.

该算法会有以下两个问题:
效率问题: 标记和清除过程的效率都不高;
空间问题: 标记清除后会产生大量不连续的内存碎片,使内存变的离散,导致在运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集.


3.3:标记-整理算法: 先标记(可达性算法)在整理,让所有存活的对象都向一端移动,然后清理掉端边界以外的内存.解决了标记-清除导致内存离散的问题。
2.4:复制算法: 将内存容量划分为两块相等的区域,每次只用其中一块,当A块满了,就触发一次GC并将存活的对象复制到B块去,然后将使用过的内存A块清理掉。该算法常用于新生代,由于新生代的对象存活时间很短,朝起夕灭。故不用按1:1比例,默认划分成了8:1:1


4.垃圾收集器

垃圾收集器是垃圾收集方法的具体实现。Serial收集器、ParNew收集器、Parallel收集器、Parallel Old 收集器、CMS收集器、G1收集器

关于这些垃圾收集器:
www.cnblogs.com/ityouknow/p…
blog.csdn.net/carson_ho/a…