解读JVM虚拟机( 三 )

  • 复制算法:其核心思想就是将内存空间分为两块 ,, 每次只使用其中一块 , 在垃圾回收时 , 将正在使用的内存中的存留对象复制到未被使用的内存块中去 , 之后去清除之前正在使用的内存块中所有的对象 , 反复去交换俩个内存的角色 , 完成垃圾收集 。 (java中新生代的from和to空间就是使用这个算法)
  • 标记压缩法:标记压缩法在标记清除法基础之上做了优化 , 把存活的对象压缩到内存一端 , 而后进行垃圾清理 。 (java中老年代使用的就是标记压缩法) 考虑一个问题:为什么新生代和老年代使用不同的算法?
  • 垃圾收集算法(二):
    • 分代算法:就是根据对象的特点把内存分成N块 , 而后根据每个内存的特点使用不同的算法 。 对于新生代和老年代来说 , 新生代回收频率很高 , 但是每次回收耗时都很短 , 而老年代回收频率较低 , 但是耗时会相对较长 , 所以应该尽量减少老年代的GC.
    • 分区算法:其主要就是将整个内存分为N多个小的独立空间 , 每个小空间都可以独立使用 , 这样细粒度的控制一次回收都少个小空间和那些个小空间 , 而不是对整个空间进行GC , 从而提升性能 , 并减少GC的停顿时间 。
    垃圾回收时的停顿现象:
    • 垃圾回收器的任务是识别和回收垃圾对象进行内存清理 , 为了让垃圾回收器可以高效的执行 , 大部分情况下 , 会要求系统进入一个停顿的状态 。
    • 停顿的目的是终止所有应用线程 , 只有这样系统才不会有新的垃圾产生 , 同时停顿保证了系统状态在某一个瞬间的一致性 , 也有益于更好地标记垃圾对象 。 因此在垃圾回收时 , 都会产生应用程序的停顿 。
    对象如何进入老年代:
    • 一般而言对象首次创建会被放置在新生代的eden区 , 如果没有GC介入 , 则对象不会离开eden区 , 那么eden区的对象如何进入老年代呢?
    • 一般来讲 , 只要对象的年龄达到一定的大小 , 就会自动离开年轻代进入老年代 , 对象年龄是由对象经历数次GC决定的 , 在新生代每次GC之后如果对象没有被回收则年龄加1.虚拟机提供了一个参数来控制新生代对象的最大年龄 , 当超过这个年龄范围就会晋升老年代 。 -XX:MaxTenuringThreshold , 默认情况下为15 。
    • 总结:根据设置MaxTenuringThreshold参数 , 可以指定新生代对象经过多少次回收后进入老年代 。 另外 , 大对象(新生代eden区无法装入时 , 也会直接进入老年代) 。 JVM里有个参数可以设置对象的大小超过在指定的大小之后 , 直接晋升老年代 。 -XX:PretenureSizeThreshold
    • 总结:使用PretenureSizeThreshold可以进行指定进入老年代的对象大小 , 但是要注意TLAB区域优先分配空间 。
    对象创建流程图:
    解读JVM虚拟机文章插图
    垃圾收集器:
    • 在java虚拟机中 , 垃圾回收器不仅仅只有一种 , 什么情况下该使用哪种 , 对性能又有什么样的影响 , 这都是我们需要了解的 。
    • 串行垃圾回收器
    • 并行垃圾回收器
    • CMS回收器
    • G1回收器
    串行回收器:
    • 串行回收器是指使用单线程进行垃圾回收的回收器 。 每次回收时 , 串行回收器只有一个工作线程 , 对于并行能力较弱的计算机来说 , 串行回收器的专注性和独占性往往有更好的性能表现 。
    • 串行回收器可以在新生代和老年代使用 , 根据作用于不同的堆空间 , 分为新生代串行回收器和老年代串行回收器 。 使用-XX:+UseSerialGC 参数可以设置使用新生代串行回收器和老年代串行回收器
    并行回收器(ParNew回收器):
    • 并行回收器在串行回收器基础上做了改进 , 他可以使用多个线程同时进行垃圾回收 , 对于计算能力强的计算机而言 , 可以有效的缩短垃圾回收所需的实际时间 。
    • ParNew回收器是一个工作在新生代的垃圾收集器 , 他只是简单的将串行回收器多线程化 , 他的回收策略和算法和串行回收器一样 。
    • 使用 -XX:+UseParNewGC 新生代ParNew回收器 , 老年代则使用串行回收器 ParNew回收器工作时的线程数量可以使用
    • -XX:ParallelGCThreads参数指定 , 一般最好和计算机的CPU相当 , 避免过多的线程影响性能 。
    并行回收器(ParallelGC回收器):