Java虚拟机学习笔记(1)

时间:2022-06-01 20:06:00

前前后后翻阅《深入理解Java虚拟机》,总是看完就忘Java虚拟机学习笔记(1)。所以打算这次学习时做好记录,以便日后查看。

一、Java内存区域:

  • 程序计数器:
    • 类似字节码的行号指示器
    • 线程私有,独立内存
    • 如果指示的是字节码,则记录正在执行的字节码指令地址;若为native方法,则记录null
    • 唯一一个在JVM规范中没有任OutOfMemoryError的区域
  • Java虚拟机栈:
    • 线程私有,生命周期与线程相同
    • 栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息
      • 局部变量表:存放着编译器可知的各种基本数据类型、对象引用和returnAddress类型(指向了下一条字节码指令的地址)
  • 本地方法栈:类似于java虚拟机栈,只不过执行的是native方法,在Sun HotSpot中将两个栈合二为一
  • Java堆:
    • 线程共享
    • 存放对象的实例
  • 方法区:
    • 线程共享
    • 存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
    • 此区域的内存回收目标主要是针对常量池的回收和对类型的卸载
  • 运行时常量池:
    • 方法区的一部分
    • 存放编译期生成的各种字面量和符号引用,类加载后进入方法区的运行时常量池中存放
    • String.intern()是在常量池增加一个对象的Unicode常量,而不是将自己的地址注册到常量池中
二、判断对象是否存活:

  • 引用计数法:给对象添加一个引用计数器,每当引用的时候,计数器+1,每当引用失效时,计数器-1
    • 缺点:很难解决对象之间相互循环引用的问题
  • 可达性分析:通过一系列的称为"GC Roots"的对象作为起点,从这些节点向下搜索引用链,当一个对象到“GC Roots”之间没有可达路径时,则证明此对象是不可用的
    • 可作为GC Roots的对象:
      • 虚拟机栈(栈帧中的本地变量表)中引用的对象
      • 方法区中类静态属性中引用的对象
      • 方法区中常量的引用的对象
      • 本地方法(Native)栈中引用的对象
  • 引用类型
三、JVM垃圾处理方法(标记清除、复制、标记整理)

  • 标记-清除法:
    • 标记阶段:先通过根节点,标记所有从根节点开始的对象,未被标记的为垃圾对象
    • 清除阶段:清除所有未被标记的阶段
    • 缺点:效率低,标记和清除效率都低;且清除后会产生大量的不连续内存碎片
  • 复制算法:
    • 将原有内存空间分为两块,每次只用其中一块,在垃圾回收时,将正在使用的内存块中存活的对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象
    • 缺点:内存使用率过小,对象存活率高的时候不适用
  • 标记-整理算法:
    • 标记阶段:先通过根节点,标记所有从根节点开始的可达对象,为被标记的垃圾对象
    • 整理阶段:将所有存活对象向一端移动,然后清理边界以外的对象
    • 缺点:相对于标记-清除法,系统清理的时间相对来说更长,也就是其他执行线程暂停的时间较长
  • 分代收集算法:
    • 将堆内存按存活时间分为新生代和老生代,新生代对象存活率低,用复制算法,老生代存活率高,用标记-清除算法或标记-整理算法。
四、新生代、老生代、持久代:

  • 新生代:在方法中new一个对象,当这个方法被调用完后,这个对象就会被垃圾回收机制回收,这是一个典型的新生代对象。
  • 老生代:
    • 在新生代中经历了N次回收后仍然存活的对象就会被放在老年代中,大对象直接进入老年代
    • 当survivor空间不够用时,需要依赖于老年代进行分配担保,所以大对象直接进入老年代
  • 永久代:方法区
五、垃圾收集器:

  • Serial收集器:
    • 单线程收集器。在进行垃圾收集时,其他线程必须暂停
    • 新生代采用复制算法,老生代采用标记算法
    • 简单高效,Client模式下默认新生代收集器
  • ParNew收集器:
    • PartNew收集器是Serial收集器的多线程版本
    • 新生代采用复制算法,老生代采用标记-清除算法
    • 它是运行在Server模式下首先新生代收集器
    • 除了Serial收集器之外,只有它能和CMS收集器配合工作
  • Parallel Scanvenge收集器
    • 类似PartNew,但更加关注吞吐量。目标是:达到一个可控制吞吐量的收集器
    • 停顿时间和吞吐量不可能同时调优。我们一方面希望停顿时间减少,另一方面希望吞吐量高,其实这是矛盾的。因为,在GC的时候,垃圾回收的工作量是不变的,如果将停顿时间减少,那频率就会提高;既然频率提高了,说明就会频繁的进行GC,那吞吐量就会减少,性能就会降低。
  • G1收集器:
    • 是当今收集器发展的是前言成果之一,对垃圾回收进行了划分优先级的操作,这种有优先级的区别方式保证了它的高效率。
    • 最大的优点是结合了空间整合,不会产生大量的碎片,也降低了进行GC的频率。
    • 让使用者明确指定停顿时间。
  • GMS收集器:
    • 一种以获取最短回收停顿时间为目标的收集器,分为初始标记、并发标记、重新标记和清除标记四个步骤,初始标记为第一次筛选,重新标记为修改后的标记记录,并发标记和并发清除会与用户线程并发执行。
    • 缺点:程序的吞吐量降低、并发执行过程中总是出现垃圾要等到下一次GC才能处理(浮动垃圾);标记-清除-空间碎片。

六、内存分配策略:

  • 对象优先在新生代Eden分配,当Eden区没有足够的空间时,则虚拟机会进行一次Minor GC,Minor GC发生在新生代,而Major GC发生在老年代,Full GC是清理整个堆空间(新生代和老生代),Major GC会伴随至少一次的Minor GC。
  • 大对象(需要很长的连续存储空间的对象,例如很长的字符串或者数组)直接进入老年代。
  • 长期存活的对象将进入老年代、虚拟机给每个对象定义了一个对象年龄计数器,每通过一次Minor GC,则计数器加1,直到到达指定阈值,就会进入老年代。
  • 如果Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象直接进入老年代。
  • 空间分配担保:在发生Minor GC之前,虚拟机会检查老年代中最大可用连续空间是否大于新生代所有对象总空间,若虚拟机设置了允许担保失败,虚拟机会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则进行一次冒险的Minor GC,如果小于或设置不允许冒险,则应该改成Full GC。