文章目录
一、jvm内存布局
(1)虚拟机栈。它保存局部变量和部分结果,并在方法调用和返回中起作用。
(2)本地方法栈。与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的 Java 方法服务,而本地方法栈则是为 Native 方法服务。
(3)程序计数器。程序计数器是一块较小的内存空间,可以看作当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变计数器的值选取下一条执行指令。分支、循环、跳转、线程恢复等功能都需要依赖计数器完成。是唯一在虚拟机规范中没有规定内存溢出情况的区域。如果线程正在执行 Java 方法,计数器记录正在执行的虚拟机字节码指令地址。如果是本地方法,计数器值为 Undefined。
(4)方法区。方法区用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
(5)堆。堆是虚拟机所管理的内存中最大的一块,被所有线程共享的,在虚拟机启动时创建。堆用来存放对象实例,Java 里几乎所有对象实例都在堆分配内存。
二、哪些对象是垃圾
1. 引用计数法
在对象中添加一个引用计数器,如果被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。原理简单,效率高,但是在 Java 中很少使用,因为存在对象间循环引用的问题,导致计数器无法清零。
2. 可达性分析法
(1)主流语言的内存管理都使用可达性分析判断对象是否存活。基本思路是通过一系列称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为引用链,如果某个对象到 GC Roots 没有任何引用链相连,则会被标记为垃圾。
(2)可作为GC ROOTS的对象
- 1)Java 虚拟机栈(局部变量表)中的引用的对象。
- 2)方法区中静态引用指向的对象。
- 3)仍处于存活状态中的线程对象。
- 4)Native 方法中 JNI 引用的对象。
三、垃圾回收算法
1. 标记-清除算法
(1)效率一般,缺点是会造成内存碎片问题。
2. 复制算法
(2)复制算法是所有算法里面效率最高的,缺点是会造成一定的空间浪费。
3. 标记-整理算法
效率比前两者要差,但没有空间浪费,也消除了内存碎片问题。
四、分代回收策略
研究表明,大部分对象的生命周期都很短,其他对象则很可能会存活很长时间。大部分死的快,其他的活的长。这个假设我们称之为弱代假设。
现在的垃圾回收器,都会在物理上或者逻辑上,把这两类对象进行区分。我们把死的快的对象所占的区域,叫作年轻代。把其他活的长的对象所占的区域,叫作老年代。
1. 年轻代
(1)年轻代分为:一个Eden区,两个Survivor区(称为S0和S1)。
(2)当 Eden 区第一次满的时候,会进行垃圾回收。首先将 Eden区的垃圾对象回收清除,并将存活的对象复制到 S0,此时 S1是空的。
(3)下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden和 S0区中所有垃圾对象清除,并将存活对象复制到 S1,此时 S0变为空。
(4)如此反复在 S0 和 S1之间切换几次(默认 15 次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将它们转移到老年代中。
(5)在这个过程中,总会有一个 Survivor 分区是空置的。Eden、from、to 的默认比例是 8:1:1,所以只会造成 10% 的空间浪费。
(6)使用复制算法进行垃圾回收。
2. 老年代
(1)一个对象如果在新生代存活了足够长的时间而没有被清理掉,则会被复制到老年代。老年代的内存大小一般比新生代大,能存放更多的对象。如果对象比较大(比如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代上。
(2)注意:对于老年代可能存在这么一种情况,老年代中的对象有时候会引用到新生代对象。这时如果要执行新生代 GC,则可能需要查询整个老年代上可能存在引用新生代的情况,这显然是低效的。所以,老年代中维护了一个 512 byte 的 card table,所有老年代对象引用新生代对象的信息都记录在这里。每当新生代发生 GC 时,只需要检查这个 card table 即可,大大提高了性能。
(3)使用标记-整理算法进行垃圾回收
五、四种引用
1. 强引用
- 处于GC ROOTS可达状态的强引用对象,即使会出现OOM,垃圾回收器也不会回收它。
Object o1 = new Object();
2. 软引用
- 如果这个对象只被软引用关联,内存够用时保留,内存实在不足时,这个对象就会被回收。
- 假如一个app需要读取大量本地图片,如果每次都从硬盘读取会严重影响性能,如果一次性全部加载到内存有可能导致内存溢出,则可用软引用解决该问题。
SoftReference<Object> objectSoftReference= new SoftReference<>(new Object());
3. 弱引用
- 在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。
WeakReference<Object> objectWeakReference= new WeakReference<>(new Object());
- WeakHashMap的键使用了弱引用的特性,当键的强引用被置为null则该键仅有弱引用,那么gc后会将该键对象回收,之后该键对应的键值对就会从map中删除。注意:如果键的类型为不可变类,键的常量池机制(如Integer的IntegerCache,字符串常量池等)会导致键的对象不能被gc回收,从而导致键值对没有被删除的情况。
//如果WeakHashMap的键的类型为Integer,避免使用Integer integer = Integer.valueOf(1),而应该使用下面的方法
Integer integer = new Integer(1);
//如果WeakHashMap的键的类型为String,避免使用String string = "abc",而应该使用下面的方法
String string = new String("abc");
4. 虚引用
- 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和ReferenceQueue联合使用。虚引用的get方法总是返回null。设置虚引用关联的唯一目的,就是在这个对象被回收器回收的时候收到一个系统通知或者后续添加进一步的处理。
jvm参数
查看jvm参数的方法
(1)jinfo -flag <具体参数名> <java进程号>
(2)jinfo -flags <java进程号>
(3)java -XX:+PrintFlagsInitial
(4)java -XX:PrintFlagsFinal -version,已修改的参数使用:=
表示,没有修改的参数使用=
表示
参数示例
-Xms
- 等价于-XX:InitialHeapSize
- 初始化堆大小
-Xmx
- 等价于-XX:MaxHeapSize
- 最大堆大小
-Xss
- 等价于-XX:ThreadStackSize
- 单个线程栈的大小
-Xmn
- 年轻代大小
-XX:MetaspaceSize
- 元空间大小
-XX:PrintGCDetails
- 打印GC详细信息
-XX:SurvivorRatio
- 新生代中eden与S0/S1空间的比例
-XX:NewRatio
- 堆中老年代与新生代空间的比例
-XX:MaxTenuringThreshold
- 年轻代中可以进入老年代的对象的年龄(0~15)