本文共 4641 字,大约阅读时间需要 15 分钟。
对于从事c/c++程序开发的开发人员来说,他们拥有内存管理的直接权利,所有内存空间的管理都交给程序员手动实现。
对于java程序员来说不再需要直接参与内存的管理,这些工作都由jvm帮我们实现。这样不容易出现“内存泄漏”等问题。这一切看起来很美好,如果不了解虚拟机怎么使用内存的,一旦遇到内存泄漏和溢出方面的问题将无从下手。
java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干不同的区域,这些区域有不同的用途与特性。根据java虚拟机规范,java所管理的内存包括以下几个运行时数据区。
1.程序计数器
2.虚拟机栈
3.本地方法栈
4.堆
5.方法区
6.运行时常量池
7.直接内存
java 中对象的回收是由jvm自动完成的,其工作区域是堆。一个通用的垃圾回收器应该需要考虑以下两个问题:
a.引用计数法
引用计数算法基本原理:
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数值减1;任何时刻计数器为0的对象是不可能再被使用的。引用计数计数算法的缺陷
class A { public Object b;}class B{ public Object a;}public class Reference { public static void main(String[] args) { A a = new A(); B b = new B(); a.b = b; b.a = a; a = null; b = null; }}
虽然a和b都被赋值为null,但是由于a和b存在循环引用,对象a和对象b的引用计数为1,这样a和b永远都不会被回收。
b.可达性分析
可达性分析算法基本原理:
通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象不可用。java语言中的GC Roots:
1.在虚拟机栈中的引用对象。 2.在方法区中的类静态属性引用的对象。 3.在方法区中的常量引用的对象。 4.在本地方法栈中JNI(native方法)的引用的对象。a.标记清除(Mark-Sweep)
标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间,过程如下图所示: 优缺点:b.标记整理(Mark-Compact)
标记整理与标记清除算法步骤一样,只是后续不是直接清除垃圾对象,而是将所有存活的对象向一端移动,然后直接清理边界以外的内存。过程如下图所示: 优缺点:c.复制算法
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。过程如下图所示: 优缺点:d.分代算法
分代算法将堆空间划分为几块,一般分为新生代和老年代,这样可以根据各个年代的特点采用最适当的收集算法。新生代中只有少量对象存活因此适合复制算法,老年代中对象存活率高,适合使用标记清除或标记整理算法。 优缺点:e.分区算法
一般来说,堆空间越大,一次GC所需要的时间就越长。分区算法将整个堆空间划分为连续的不同小区域,每个小区独立使用,独立回收。 优缺点:a.强引用
特点: 1.强引用可以直接访问目标对象。 2.强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会强行回收引用所指向的对象。public class StrongRef { public static void main(String[] args) { String str1 = new String("hello world"); String str2 = str1; str1 = null; System.out.println("GC前: " + str2); System.gc(); System.out.println("GC后: " + str2); }}
运行结果:
GC前: hello worldGC后: hello world
b.软应用
特点: 当内存资源不足时,软引用对象会被回收,因此软引用不会导致OOM。import java.lang.ref.SoftReference;class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; }}public class SoftRef { public static void main(String[] args) { StringBuilder builder = new StringBuilder(); for (int i = 0 ;i < 1024 * 10; ++i) { builder.append("hello"); } User user = new User(builder.toString(), 1); SoftReferencestringSoftReference = new SoftReference<>(user); user = null; System.out.println(stringSoftReference.get()); System.gc(); System.out.println("First GC:"); System.out.println(stringSoftReference.get()); byte[] b = new byte[1024 * 400]; System.gc(); System.out.println("Second GC:"); System.out.println(stringSoftReference.get()); }}
设置运行参数-Xmx1M
运行结果:reference.User@38af3868First GC:reference.User@38af3868Second GC:null
c.弱引用
特点:只要发生GC,无论内存资源怎样,弱引用对象都会被回收。public class WeakRef { public static void main(String[] args) { String str = new String("hello world"); java.lang.ref.WeakReferenceref = new java.lang.ref.WeakReference (str); str = null; System.out.println("GC前: " + ref.get()); System.gc(); System.out.println("GC后: " + ref.get()); }}
运行结果:
GC前: hello worldGC后: null
d.幽灵引用
特点:一个持有幽灵引用的对象和没有引用几乎一样,随时都可能被垃圾回收器回收。