JVM内存模型–对象生命周期
思考: 什么时候才会进行垃圾回收?
关注内存的回收,是选择收的多还是选择垃圾回收线程耗时短
回收时间短到何种程度,1次网络延迟时间是多久
CPU使用率很高的情况下,适当降低垃圾回收的频率,有一系列的问题。
对象的生命周期
finalize方法Demo
public class Finalize {
private static Finalize save_hook = null;//类变量
public void isAlive() {
System.out.println("我还活着");
}
@Override
public void finalize() {
System.out.println("finalize方法被执行");
Finalize.save_hook = this;
}
public static void main(String[] args) throws InterruptedException {
save_hook = new Finalize();//对象
//对象第一次拯救自己
save_hook = null;
System.gc();
//暂停0.5秒等待他
Thread.sleep(500);
if (save_hook != null) {
save_hook.isAlive();
} else {
System.out.println("好了,现在我死了");
}
//对象第二次拯救自己
save_hook = null;
System.gc();
//暂停0.5秒等待他
Thread.sleep(500);
if (save_hook != null) {
save_hook.isAlive();
} else {
System.out.println("我终于死亡了");
}
}
}
- 创建阶段
(1)为对象分配存储空间
(2)开始构造对象
(3)从超类到子类对static成员进行初始化
(4)超类成员变量按顺序初始化,递归调用超类的构造方法
(5)子类成员变量按顺序初始化,子类构造方法调用,并且一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段 - 应用阶段
(1)系统至少维护者对象的一个强引用
(2)所有对该对象的引用全部都是强引用(除非显式的使用了:软引用,弱引用,虚引用)
引用的定义:
1、我们的数据类型必须是引用类型
2、我们这个类型的数据所存储的数据必须是另一块内存的起始地址
引用:
1、强引用:
JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径,当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用
2、软引用:
软引用时用来描述一些还有用但是非必须的对象。对于软引用关联的对象,在系统将发生内存溢出异常之前,将会把这些对象列入回收范围中进行二次回收
(当你去处理占用内存较大的对象,并且生命周期比较长的,不是频繁使用的)
问题:软引用可能会降低应用的运行效率与性能。比如:软引用指向的对象如果初始化很耗时,或者这个对象在进行使用的时候被第三方施加了我们未知的操作。
3、弱引用:
弱引用对象与软引用对象的最大不同在于:GC在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用对象,GC总是进行回收,因此弱引用对象会更容易,更快被GC回收
4、虚引用:
也叫幽灵引用或幻影引用,为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知,也就是说,如果一个对象被设置上了一个虚引用,实际上跟没有设置引用没有任何区别
软引用Demo
public class SoftReferenceDemo {
public static void main(String[] args) {
//。。。一堆业务代码
Worker a = new Worker();
//。。业务代码使用到了我们的Worker实例
// 使用完了a,将它设置为soft 引用类型,并且释放强引用;
SoftReference sr = new SoftReference(a);
a = null;
//这个时候他是有可能执行一次GC的
System.gc();
// 下次使用时
if (sr != null) {
a = (Worker) sr.get();
System.out.println(a );
} else {
// GC由于内存资源不足,可能系统已回收了a的软引用,
// 因此需要重新装载。
a = new Worker();
sr = new SoftReference(a);
}
}
}
弱引用Demo
public class SoftReferenceDemo {
public static void main(String[] args) {
//。。。一堆业务代码
Worker a = new Worker();
//。。业务代码使用到了我们的Worker实例
// 使用完了a,将它设置为soft 引用类型,并且释放强引用;
SoftReference sr = new SoftReference(a);
a = null;
//这个时候他是有可能执行一次GC的
System.gc();
// 下次使用时
if (sr != null) {
a = (Worker) sr.get();
System.out.println(a );
} else {
// GC由于内存资源不足,可能系统已回收了a的软引用,
// 因此需要重新装载。
a = new Worker();
sr = new SoftReference(a);
}
}
}
虚引用Demo
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
Object value = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Thread thread = new Thread(() -> {
try {
int cnt = 0;
WeakReference<byte[]> k;
while ((k = (WeakReference) referenceQueue.remove()) != null) {
System.out.println((cnt++) + "回收了:" + k);
}
} catch (InterruptedException e) {
//结束循环
}
});
thread.setDaemon(true);
thread.start();
Map<Object, Object> map = new HashMap<>();
for (int i = 0; i < 10000; i++) {
byte[] bytes = new byte[1024 * 1024];
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes, referenceQueue);
map.put(weakReference, value);
}
System.out.println("map.size->" + map.size());
}
}
-
不可见阶段
不可见阶段的对象在虚拟机的对象根引用集合中再也找不到直接或者间接的强引用,最常见的就是线程或者函数中的临时变量,程序不在持有对象的强引用(但是某些类的静态变量或者JNI是有可能持有的) -
不可达阶段
指对象不在被任何强引用持有,GC发现该对象已经不可达
如果确定一个对象是垃圾
引用计数法
对于某个对象而言,只要应用程序中持有该对象的引用,就说明该对象不是垃圾,如果一个对象没有任何指针对其引用,他就是垃圾
**弊端:**如果AB对象互相持有,会造成循环引用问题,导致永远不能被回收
可达性分析
通过GC Root对象,开始向下存照,看某个对象是否可达,如果不可达则认为其是垃圾
能作为GC Root:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等,GC Root本质上是一组活跃的引用
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(即一般说的Native方法)引用的对象
- 收集阶段
GC发现对象处于不可达阶段并且GC已经对该对象的内存空间重新分配做好准备,对象就进入收集阶段,如果该对象的finalize()方法被重写,则执行该函数
1、会影响到JVM的对象以及分配回收速度
2、可能造成对象再次复活
- 终结阶段
对象的finalize()方法指向完成之后,对象如果仍处于不可达状态,该对象进入终结阶段 - 对象内存空间重新分配阶段
GC对该对象占用的内存空间进行回收或者再分配,该对象彻底消失