初识JVM自动内存管理机制

17 篇文章 0 订阅
4 篇文章 0 订阅

参考资料(视频)

https://ke.qq.com/course/331125?_bid=167&_wv=3&from=Mobile_qqchat

Java虚拟机基本结构

  • 类加载子系统与方法区:类加载子系统负责从文件系统或网络中加载 Class 信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字符串字面量数字常量(这部分常量信息是 Class 文件中常量池部分的内存映射)。方法区为线程共享
  • Java 堆:Java 堆在虚拟机启动的时候建立,它是 Java 程序最主要的内存工作区域。几乎所有的 Java对象实例都存放在 Java 堆中。堆空间是所有线程共享的,这是一块与 Java 应用密切相关的内存空间
  • 直接内存:Java 的 NIO 库允许Java程序使用直接内存。直接内存是在Java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于Java堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。由于直接内存在Java堆外,因此它的大小不会直接受限于 Xmx 指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存
  • 垃圾回收系统:垃圾回收系统是Java虚拟机的重要组成部分,垃圾回收器可以对方法区、java堆和直接内存进行回收。其中,java堆是垃圾回收器的工作重点和C/C++不同,Java中所有的对象空间释放都是隐式的,也就是说,Java中没有类似free()或者delete()这样的函数释放指定的内存区域。对于不再使用的垃圾对象,垃圾回收系统会在后台默默工作,默默查找、标识并释放垃圾对象,完成包括Java堆、方法区和直接内存中的全自动化管理
  • Java栈:每一个Java虚拟机线程都有一个私有的Java栈,一个线程的Java栈在线程创建的时候被创建,Java栈中保存着帧信息、局部变量、方法参数,同时和Java方法的调用、返回密切相关
  • 本地方法栈:本地方法栈和Java栈非常类似,最大的不同在于Java栈用于Java方法的调用,而本地方法栈则用于本地方法(由关键字 native 修饰的方法)的调用,作为对Java虚拟机的重要扩展,Java虚拟机运行Java直接调用本地方法(通常用C编写)
  • 程序计数器:程序计数器也是每一个线程私有的空间,Java虚拟机会为每一个Java线程创建一个程序计数器,在任意时刻,一个Java线程总是在执行一个方法,这个被执行的方法称为当前方法。如果当前方法不是本地方法,程序计数器就会指向当前正在被执行的指令;如果当前方法是本地方法,那么程序计数器的值就是 undefined
  • 执行引擎:执行引擎是Java虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提高执行效率,会使用即时编译技术(JIT)将方法编译成机器码后再执行

JVM堆结构图及分代

  • 内存分代示意图:

  • JVM内存分代策略:Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般为新生代老年代和永久代(对HotSpot虚拟机而言,可能之后这个代会被去除,jdk8已经移除永久代)
  • 为什么要分代?为了提高对象内存分配核垃圾回收的效率
  • 新创建的对象会在新生代中分配内存,经过多次回收仍然存活下来的对象存放在老年代中。静态属性、类信息等存放在永久代中。新生代中的对象存活时间短,只需要在新生代区域中频繁进行GC;老年代中对象生命周期长,内存回收的频率相对较低,不需要频繁进行回收;永久代回收效果太差,一般不进行垃圾回收
  • 新生代(Young Generation):新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代)。当Eden区没有足够的空间进行分配时,虚拟机将发起一次 Minor GC。GC开始时,对象只会存在于Eden区和From区,To区是空的(作为保留区域)。GC进行时,Eden区中所有存活的对象都会被复制到To区。而在From区中,仍存活的对象会根据他们的年龄值决定去向,年龄值达到年龄阈值(默认为15,新生代中的对象每熬过一轮垃圾回收,年龄值就加1,GC分代年龄存储在对象的header中)的对象会被移到老年代中,没有达到阈值的对象会被复制到To区接着清空Eden区和From区,此时新生代中存活的对象都在To区。接着,From区和To区交换它们的角色,也就是新的To区就是上次GC清空的From区,新的From区就是上次GC的To区。总之,不管怎样,都会保证To区在一轮GC后总是空的。GC时当To区没有足够的空间存放上一次新生代收集下来的存活对象时,需要依赖老年代进行分配担保,将这些对象存放在老年代中(担保机制)
  • 老年代(Old Generation):在新生代中经历了多次(具体看虚拟机配置的阈值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢
  • 永久代(Permanent Generation):永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收

JVM垃圾回收算法

  • 引用计数算法(Reference Counting):比较古老的回收算法。原理是此对象有一个引用,则增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题
  • 复制算法(Copying):此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用种的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此算法成本比较小,同时复制过去以后还能进行相应的内存管理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间
  • 标记-清除算法(Mark-Sweep):此算法执行分两阶段,第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除此算法需要暂停整个应用(STW),同时,会产生内存碎片
  • 标记-整理算法(Mark-Compact):此算法结合了“复制”“标记-清除”两个算法的优点,也是分两个阶段。第一阶段从根节点开始标记所有被引用的对象,第二阶段遍历整个堆,清除未标记对象并把存活对象“压缩”到堆中的其中一块按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题

JVM收集器

  • Scavenge GC(次收集)和 Full GC(全收集)的区别:
  1. 新生代GC(Scavenge GC):也称 Minor GC,指发生在新生代的GC。因为新生代的Java对象大多都是朝生夕死,所以Scavenge GC非常频繁,一般回收速度也比较快。当Eden区空间不足以为对象分配内存时,会触发Scavenge GC
  2. 老年代GC(Full GC):也称 Major GC,指发生在老年代的GC。出现了Full GC一般会伴随着至少一次的Minor GC(老年代的对象大部分是Minor GC过程中从新生代进入老年代的),比如:担保失败(老年代内存不足时担保失败)。Full GC的速度一般会比Minor GC慢10倍以上,当老年代内存不足或者显式调用System.gc()方法时,会触发Full GC
  • 新生代收集器:
  1. 串行收集器(Serial):Serial 收集器是HotSpot 运行在 Client 模式下的默认新生代收集器,它的特点是只用一个CPU/一条收集线程使用复制算法去完成GC工作,且在进行垃圾收集时必须暂停其他所有的工作线程(Stop The World,STW),可以使用参数-XX:UseSerialGC 打开
  2. 并行收集器(ParNew):ParNew 收集器其实是前面 Serial 的多线程版本,除使用多条线程进行GC外,其余特点都与Serial算法相同。若老年代收集器使用的是CMS,则ParNew将是默认的新生代收集器
  3. Parallel Scavenge 收集器:与ParNew类似,Parallel Scavenge 也是使用复制算法,也是并行多线程收集器,但与其它收集器关注尽肯能缩短垃圾收集时间不同,它更关注系统吞吐量停顿时间越短就越适用于用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务,可以最高效率地利用CPU时间,尽快地完成程序的运算任务
  • 老年代收集器:
  1. Serial Old 收集器:为 Serial 收集器的老年代版本,同样是单线程收集器,使用“标记-整理”算法
  2. Parallel Old 收集器:为Parallel Scavenge的老年代版本,使用多线程和“标记-整理”算法,吞吐量优先主要与Parallel Scavenge配合
  3. CMS(Concurrent Mark Sweep)收集器:真正意义上的并发收集器,以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法实现。整个GC过程分为以下4个步骤:初始标记(STW)并发标记重新标记(STW)并发清除
  • G1(Garbage First)收集器:不属于分代收集器,新老通用。G1将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合
  • JVM中并行和并发:并行指GC多线程运行(与用户线程无关),并发指GC线程与用户线程一同运行
  • JVM优化的思路:
  1. 选择JVM版本(Client 或者 Server)
  2. 设置堆区大小的分配
  3. 选择合适的垃圾回收器
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值