很久没写博客了,工作了忙起来也没啥时间,最近在面试,也在看一部分jvm相关的东西,毕竟面试都会有人问到这些,作为菜鸟就要多学点了解下。
博客都是个人观点有错误地方望指出。jvm 是由堆,栈,本地方法栈,方法区组成。
堆(存放new出来的对象):分为:新生代,旧生代,持久带。从字面意思理解,刚创建的对象肯定是放在新生代的,旧生代应该是放一些存活着的对象,持久带肯定就是放一些类、方法的信息,还有一些常量。
新生代(年轻代):分为 eden space 和 survivor space 两个区域,新生成的对象都是由新生代在控制放在eden space区, 当eden space 区内存不够时,就会移动一些在里面一直存在的对象到 survivor space区。新生代可以用 -Xmn(-Xms控制初始化堆大小,-Xmx 控制堆的最大值,后续会详细说明)控制,也可以使用-XX:eden pace / survivor space 的比例控制。survivor space还由 from space 和 to space两个组成。
旧生代(老年代):用于存放经过gc多次回收仍然存在的对象。
持久带:是hotspot jvm 利用旧生代实现的方法区,有些jvm没有旧生代则采用其他方式实现方法区, 不等同于方法区,可以用通过-XX:PermSize -Xms:MaxPermSize来控制初始值和最大值
-Xmx: 最大堆内存 如:-Xmx1024m
-Xms: 初始化堆内存 如:-Xms128m
-XX:MaxNewSize:最大新生代内存
-XX:NewSize:初始化新生代内存,NewSize通常情况为 Xmx的1/3或1/4,新生代=eden + 2 * survivor的大小,可用空间为 eden + 1 * survivor 大小
-XX:PermMaxSize :最大持久代内存
-XX:PermSize:初始化持久代内存
-XX:+PrintGCDetails:打印 gc 信息
-XX:NewRatio:新生代和旧生代的比例,如 -XX:NewRatio=3,则新生代占堆内存的1/4,旧生代占堆内存的3/4
-XX:SurvivorRatio 新生代Eden 与 Survivor的比值,默认值为8,表示Eden占新生代的8/10,Survivor占新生代的1/10( 有两个Survivor)
栈:
每个线程执行每个方法的时候都会在栈中申请一栈帧,每个栈帧包括局部变量区和操作数栈,用来存放方法调用过程中的临时变量、参数和中间结果。
-xss:设置每个线程的堆栈大小,jdk1.5以后,每个线程堆栈大小为1m,正常情况都是够用的。
本地方法栈:
用于存放支持 native 方法的执行,存储了每个native方法调用的状态
方法区
存放了要加载的类信息,静态变量,final类型的常量,属性和方法信息, jvm用持久代 来存放方法区,可通过 -XX:PermSize 和 -XX:MaxPermSize 控制初始值和最大值
垃圾回收按照回收策略分
引用计数
比较老的回收算法,一个对象有一个引用增加一个计数,删除一个引用则减少一个计数,回收时,只用收集计数为0的对象,缺点:无法处理循环引用的问题。
标记-清除
分为两个阶段,第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除,缺点:需要暂停整个应用,产生内存碎片。
复制
把内存空间划为两个相等的区域,每次只使用其中一个区域,垃圾回收时,遍历当前使用区域,把政治使用中的对象复制到另外一个区域中,算法每次只处理正在被使用中的对象,复制成本较小,同事复制过去以后还能进行相应的内存整理,不会出现碎片问题,缺点:需要占用两倍的内存空间
标记-整理
结合了标记-清除和复制两个算法的优点,分两个阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且存活对象压缩到堆的其中一块,按顺序排放,避免了标记-清除的碎片问题,避免了 复制算法的空间问题。
jvm分别对新生代和旧生代采用不同的垃圾回收机制
新生代的gc
新生代存活时间较短,因此机遇复制算法来进行回收,复制算法就是扫描存活对象,并复制到一块新的完全未使用的空间中,对于新生代,就是在 eden 和 from space 或 to space 之间 复制,新生代采用空间指针的方式来控制gc触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够久出发gc,当连续分配对象时,对象会逐渐从eden 到survivor,最后到旧生代
在执行机制上 jvm提供了串行gc(serial gc),并行回收gc(parallel scavenge), 并行gc(parnew)
串行gc
在整个扫描和复制过程采用单线程方式进行,适用于单cpu,新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的gc方式, 可以通过 -XX:+UseSerialGC来强制制定
并行回收gc
在整个扫描和复制过程中采用多线程的方式进行,适用于多cpu,对暂停时间要求较短的应用上,是server级别默认采用的gc方式,可用 -XX:+UseParallelGC来强制指定,
用-XX:+UseParallelGC来强制制定,用-XX:ParallelGCThreads=4来指定线程数
并行gc
与旧生代的并发gc配合使用
旧生代的gc
指定方式 | 新生代GC方式 | 旧生代GC方式 |
-XX:+UseSerialGC | 串行GC | 串行GC |
-XX:+UseParallelGC | 并行回收GC | 并行GC |
-XX:+UseConeMarkSweepGC | 并行GC | 并发GC |
-XX:+UseParNewGC | 并行GC | 串行GC |
-XX:+UseParallelOldGC | 并行回收GC | 并行GC |
-XX:+ UseConeMarkSweepGC -XX:+UseParNewGC | 串行GC | 并发GC |
不支持的组合 | 1、-XX:+UseParNewGC -XX:+UseParallelOldGC 2、-XX:+UseParNewGC -XX:+UseSerialGC |