深入理解JAVA虚拟机——总结1:自动内存管理机制

JAVA虚拟机管理的内存

  • 数据区

    • 线程共享部分

      • 本地方法区(存放虚拟机加载的类信息、常量、静态变量、JIT编译器编译后的代码等)(有时称为永久代,逐渐被取代)
        StackOverflowError: 动态生成大量Class(CGLib,jsp)时,类回收情况
        • 运行时常量池(存放编译器生成的各种字面量和符号引用,具备动态性,可以在运行期间将新的常量加入池中,String.intern()方法)
      • (GC堆,存放对象实例,逻辑上连续、物理上不连续,可扩展)(可能划分出线程私有的分配缓冲区TLAB
        溢出: 对象数量达到最大堆的容量限制(区分内存溢出和内存泄漏:泄漏的对象是指已经死亡但未被回收的对象,溢出的对象是仍然存活的对象)
        • 新生代
          • Eden(伊甸)空间
          • From Survivor空间
          • To Survivor空间
        • 老年代
    • 线程私有部分

      • 程序计数器(当前线程执行的字节码行号指示器,改变数值读取下一条指令;唯一没有规定OutOfMemoryError的区域)
      • 虚拟机栈(JAVA方法执行的内存模型,每个方法对应一个栈帧,栈帧内保存该方法的局部变量表、操作数帧、动态链接、方法出口)
        StackOverflowError: 线程请求的栈深度大于虚拟机所允许的最大深度,单线程下栈帧太大或机栈容量太小会引起该异常
        OutOfMemoryError: 扩展栈时无法申请到足够的内存空间,过多线程会引起该异常,减少最大堆或者减少栈容量可以换取更多线程

      • 本地方法栈(Native方法的内存模型,类似虚拟机栈,二者可以合二为一)

  • 执行引擎
  • 本地库接口
  • 直接内存非虚拟机内存,NIO类使用Native函数库直接分配堆外内存,通过存储在JAVA堆中的DirectByteBuffer对象作为这个对象的引用进行操作,避免在Java堆和Native堆中来回复制数据)

垃圾回收

  • 判断对象是否还活着
    • 引用计数算法(产生一个引用则计数器加一,计数器为0的对象不再使用;无法解决相互循环引用的问题)
    • 可达性分析算法(主流算法,判断一个对象与GC Root之间是否有引用链相连)
      • GC Root: 虚拟机栈中引用的对象,本地方法栈中Native方法引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象
  • 垃圾收集算法
    • 标记-清除算法(mark-sweep):产生大量不连续的内存碎片,无法找到足够的连续内存来存放较大的对象,不得不触发另一次GC。
    • 复制算法(copying): 将内存分为两部分,A用于分配对象,B闲置,GC时将分配了A中仍然存活的对象复制到B中,新建的对象仍分配在A中
      • 分配担保机制:新生代中Eden+From survivor相当于内存A部分,To survivor相当于B部分,如果B不够用,则可以复制到老年代,此时老年代就对新生代进行了分配担保
    • 标记-整理算法(mark-compact): 存活的对象向一端移动,清理掉存活对象以外的内存
    • 分带收集算法(generational collection)
      • 新生代:复制算法,存活率低,复制成本低
      • 老年代: 标记-清理,标记-整理算法,存活率高,没有额外空间做分配担保
      • 永久代(方法区)
        • 废弃常量:如果没有地方引用常量(如字面量“abc”),则该常量就会被系统清理出常量池
        • 无用的类: 满足3个条件,该类的所有实例都被回收,加载该类的ClassLoader被回收,该类对应的java.lang.Class对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法,则卸载类
  • 垃圾收集器
    • 新生代
      • Serial(Client模式下默认的新生代收集器,适用于单个CPU环境): 单线程收集垃圾,暂停用户工作线程
      • ParNew(Serial的多线程版本,Server模式下首选的新生代收集器): 多线程并行收集垃圾,暂停用户工作现程
      • Parallel Scavenge(可控制吞吐量的收集器,适用于后台运算不需要太多交互的任务):有用参数—GCTimeRatio(吞吐量的倒数)、UseAdaptiveSizePolicy(提供一个优化目标如吞吐量或是最大停顿时间,虚拟机可以自适应调节细节参数)
        • GC停顿时间的缩短是以牺牲吞吐量和新生代空间来换取的,新生代越小GC越快,吞吐量=运行用户代码时间/(垃圾收集时间+运行用户代码时间)越小
    • 老年代
      • Serial Old(Serial的老年代版本):单线程、标记-整理
      • Parallel Old(Parallel的老年代版本):单线程、标记-整理
      • CMS(concurrent mark sweep):以回收停顿时间最短为目标,应用广泛,标记-清除算法
        • 步骤: 初始标记(STW,标记GC Root直接关联的对象,速度快),并发标记(GC Root tracing,与用户工作线程同时执行),重新标记(STW,修正并发标记期间产生变动的对象标记记录,时间长于初始标记,短于并发标记),并发清除(标记-清除)
        • 缺点: CPU数量不足4个时,CMS对用户程序影响较大,占用CPU资源,导致应用程序变慢;无法处理浮动垃圾(并发清理过程中用户线程产生的新的垃圾);不能等老年代几乎填满了再收集,需要预留一部分空间供并发收集时程序运行使用;标记-清除,内存碎片,内存整理无法并发完成
    • G1:并行与并发,分代收集(采用不同的方法处理新创建的对象和存活一段时间的对象) ,空间整合(整体上是标记-整理,局部看是复制算法,无内存碎片),可预测的停顿
      • 将JAVA堆划分成多个等大的独立区域region,每个region对应一个remebered set,其中存储的是该region中的reference引用的属于其他的region的对象的引用信息
      • 步骤:初始标记(STW,标记GC Root直接关联的对象,速度快),并发标记(GC Root tracing,与用户工作线程同时执行),最终标记(STW,修正并发标记期间产生变动的对象标记记录,时间长于初始标记,短于并发标记,remebered set,并行执行),筛选回收(对各个region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定回收计划)
  • 内存分配和回收策略
    • 如果启动本地线程分配缓冲,则对象优先按线程分配在TLAB上
    • 优先分配在Eden区
    • Eden区空间不足,则minor GC(较频繁、速度快,处理新生代),存活对象存入survivor区,年龄设为1
      • 在survivor区每熬过一次minor GC,则年龄+1,年龄足够大后移至老年代(MaxTenuringThreshold设置年龄)
      • survivor空间中同龄对象数量占多一半时,大于该年龄的对象直接存入老年代
    • 大对象直接存入老年代,避免大量内存复制
    • 老年代最大可用连续空间不足以存放新生代对象时,出发Full/Major GC(常伴随minor GC,速度慢,处理新生代+老年代)
  • HotSpot的算法实现

    • 枚举GC Root –> OopMap(ordinary object pointer map)数据结构(在特定位置记录下站和寄存器中哪些位置是引用)

      GC Root: 虚拟机栈中引用的对象,本地方法栈中Native方法引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象

    • 安全点:HotSpot并不为每条指令生成OopMap,所谓的特定位置就是safepoint。

      • 程序只能在到达安全点时才能暂停,执行GC,所以安全点不能太少(让GC等待太久)也不能太多(增大运行时负荷)
      • 安全点选定基准:让程序长时间执行,特征是:指令序列复用(如,方法调用、循环跳转、异常跳转等)
      • 程序中断方案
        • 抢先式中断:GC发生时,所以线程全部中断,中断位置不是安全点的线程再恢复运行直至到达安全点
        • 主动式中断:GC发生时,先设置一个标志,所有线程到达每个安全点时查询该标志是否被设置,如果被设置则中断,否则继续运行
    • 安全区域:一段引用关系不会发生变化的代码片段,GC安全
      • 进入安全区域后,标识自身,GC发生时,不会中断这些线程,当线程要离开安全区域时,检查GC是否完成,未完成则等待,完成则继续运行
      • 用于解决一旦线程sleep或block,则无法像安全点中断方案所说的运行到安全点的问题

性能监控与故障处理工具

  • jps:查询正在运行的虚拟机进程的ID号
  • jstat: 查询虚拟机信息,如类装载、卸载数量、空间、耗时,垃圾收集各个区域的空间、耗时、GC原因等,运行期编译的方法、耗时等
  • jinfo:java虚拟机参数列表、配置信息
  • jmap: 生成dump文件,查询finalize执行队列、Java堆和永久代使用率、收集器等详细信息
  • jstack: 堆栈跟踪工具,生成线程快照,定位线程长时间停顿原因
  • 可视化工具:jconsole,visualVM,memory analyzer tool,jrockit mission control
    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/LL1256/article/details/53306960
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞