JVM学习
JVM入门(一)
JVM 结构
类加载子系统 和 方法区
类加载子系统:负责从文件系统和网络中加载 Class信息,加载的类信息存放在一块 -> 称为方法区
除了类的信息外,方法区还存放运行时常量池信息,包括字符串面量,数字常量(这部分常量信息是Class文件中常量池部分内存映射)Java堆 *
Java堆在虚拟机启动时建立,它是Java程序最主要的内存工作区直接内存
- 垃圾回收机制 *
- Java栈
每个Java虚拟机线程都有一个私有的Java栈,一个线程的Java栈在线程创建的时候被创建,Java栈中保存着帧信息,
Java栈中保存着局部变量,方法参数,同时和Java方法的调用、返回密切相关 - 本地方法栈
本地方法栈和Java栈十分类似,不同在
作为对Java虚拟机的重要拓展,Java虚拟机允许Java直接调用本地方法(通常是c写的)Java栈用于方法调用 本地方法栈用于本地方法调用 - PC(Program Counter)
PC寄存器也是每个线程私有的空间创建(跟Java栈相似),Java虚拟机会给每个Java线程创建PC寄存器。在任意时刻,一个Java线程总是在执行一个方法,
这个正在执行方法是当前方法。如果当前方法是本地方法,那么PC寄存器的值是 undefined 如果是Java方法,PC寄存器就会指向当前正在被执行的指令 - 执行引擎
执行引擎是Java虚拟机最核心组件之一,负责执行虚拟机的字节码,现在虚拟机为了提高执行效率,会使用即时编译(Just In Time JIT)技术将方法编译成机器码后再执行基本结构.png
JVM堆结构图 和 分代
java堆结构.png
Java虚拟机根据对象存活周期不同,把堆内存分成几块,一般分为新生代,老年代,永久代(对于HotSpot虚拟机而言),JVM就是按照这个策略划分周期
为什么要分代?
堆内存是虚拟机管理的内存中最大的一块,垃圾回收最频繁的一块,程序中所有对象实例都存放在堆内存中。给堆内存分代是为了提高对象内存分配和垃圾回收效率
如果不划分,所有新创建的对象和生命周期很长的对象放在一起,随着程序执行,堆内存需要频繁进行垃圾收集,每次回收都要遍历所有的对象
遍历对象会浪费巨大的时间代价,影响GC效率
有了分代的情况,新创建的对象会在新生代中分配内存,经过多次回收依然存活的对象放在老年代中,静态属性、类信息方法老年代中。
新生代中对象存活时间短,只需要在新生代中频繁进行GC
老年代中生命周期长,GC频率低,
永久代中一般不进行垃圾回收
根据不同年代的特点采用适合的垃圾回收算法,分代收集很大提升了收集效率,这都是内存分代的好处
内存分代划分
Java虚拟机将堆内存分为新生代、老年代、永久代,永久代是HotSpot VM特有概念
永久代主要存放常量、类信息、静态变量和垃圾回收关系不大
新生代(Yong Generation)
新生成的对象优先存在新生代中,新生代对象存活率低,在新生代中,回收效率高。一般回收70%~95%
新生代划分为三块,Eden空间和两个较小Survivor空间,默认比例为 8:1:1。
通过 复制算法 进行
虚拟机配置阀值默认是15 进入老年代
老年代(Old Generation)
在新生代中经历多次(虚拟机配置阀值)GC后仍然存活的对象进入老年代,老年代中的对象生命周期长,存活率高,在老年代中进行GC频率相对低,而且回收速度慢
永久代(Permanent Generation)
永久代存储类信息、常量、静态常量
JVM垃圾回收算法 和 收集器
1 垃圾回收常见算法
- 引用计数法
- 复制算法(Coping)
算法把内存空间划分成两个相等区域,每次只使用一个区域,垃圾回收时,遍历当前使用区域,把当前使用中的对象复制到另外一个区域中。
新生代中的survivor区次算法每次只处理正在使用中的对象,因此复制成本小,同时复制过去以后还能进行`相应的内存整理`,不会出现“碎片”问题。此算法`缺点是需要两倍内存空间`复制算法图解
标记-清除(Mark-Sweep)
算法执行分两个阶段,- 第一个阶段:从引用根节点开始标记所有被引用的对象
- 第二个阶段:遍历整个堆,把未标记对象清除
此算法需要暂停整个应用,同时,会产生内存碎片标记清除.png
标记-整理(Mark-Compact)
算法结合了“标记-清除”和“复制”两个算法优点。分为两个阶段- 第一个阶段:从根节点开始标记所有被引用对象
- 第二个阶段:遍历整的堆,把清除未标记对象并且把对象“压缩”到堆中的其中一块,按顺序排放,
这个算法避免了“标记-清除”的碎片问题,也避免了“复制”算法的空间问题标记整理.png
2 JVM垃圾收集器
Scavenge GC(次收集)和Full GC(前收集)
新生代 GC(Scavenge GC):Scavenge GC指发生在新生代的GC,因为新生代的Java对象大多都是存活时间短,所以Scavenge GC非常频繁,回收速度也比较快。当Eden空间空间不足给对象分配内存时,会触发Scavenge GC。
一般情况下,当新对象生成,并且在Eden 申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区,这种GC方式是对年轻代的Eden区进行,不会影响老年代。因为大部分对象都是从Eden区开始,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而一般需要使用速度快、效率高的算法,
使Eden去尽快空闲出来老年代GC(Full GC/Major GC):Full GC指发生在老年代的GC,出现了Full GC,一般会伴随至少一次的Minor GC(老年代的对象大部分是Minor GC过程中从新生代进入老年代),比如分配担保失败。Full GC的速度一般会比Minor GC慢10倍以上。
当老年代内存不足或者显示调用System.gc() 方法,会触发Full GC
小结:
- Scavenger GC:当年轻代堆内存紧张会触发,相对于全收集来说,收集间隔短
- Full GC:当老年代或永久代堆满了,会触发Full GC可以使用 System.gc() 来显示启动Full GC,Full GC一般根据堆大小的不同,需要的时间不相同,但一般会比较长,超过3-5秒已经算长了
3
- 本文作者:Jun
- 本文链接:http://mambajun.github.io/2019/12/27/JVM%E5%AD%A6%E4%B9%A0/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!




