Java虚拟机--判断可触及性(七)
来源:互联网 发布:强制锁定4g网络的软件 编辑:程序博客网 时间:2024/06/16 12:39
知识点的梳理:
- 可触及性包含3种状态:对象只有在不可触及状态时才会被回收!;
- 可触及的:从根节点开始,可以到达这个对象;
- 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活;
- 不可触及的:对象的finalize()函数被调用,并且没有复活,就会进入不可触及状态。该状态的对象不可能被复活,因为finalize()函数只会被调用一次;
- Java提供4个级别的引用:强引用,软引用,弱引用,虚引用;
- 说明
- 可触及性的必要性:不被使用的对象,很有可能借助finalize()函数复活自己,所以我们需要一个状态来判定一个对象到底是否可回收!
- 示例1:对象的复活
public classCanReliveObj {
public staticCanReliveObjobj;
@Override
protected void finalize()throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj =this;
}
@Override
public String toString() {
return"I am CanReliveObj";
}
public static void main(String[]args) throws InterruptedException {
obj =new CanReliveObj();
//第一次清除对象,对象通过finalize()复活
obj =null;
System.gc();
Thread.sleep(1000);
if(obj ==null){
System.out.println("obj是null");
}else{
System.out.println("obj可用");
}
System.out.println("第2次GC");
//第二次清除对象,无法再复活
obj =null;
System.gc();
Thread.sleep(1000);
if(obj ==null){
System.out.println("obj是null");
}else{
System.out.println("obj可用");
}
}
}
- 引用和可触及性的强度
- 软引用,弱引用,虚引用可在java.lang.ref包中找到它们。FinalReference表示"最终"引用,它用于实现对象的finalize()方法
- 强引用
- 能力:程序中一般的引用类型,此级别的对象是可触及的,不会被回收;
- 能力特点:
- 可直接访问目标对象;
- 强引用指向的对象在任何时候都不会被回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向的对象;
- 强引用可能导致内存泄漏;
- 示例:
步骤1 : StringBuffer str = new StringBuffer("Hello World");
说明:假设此段代码在函数体内运行,那么局部变量str将被分配在栈上,而对象StringBuffer实例被分配在堆上。局部变量str指向StringBuffer实例所在堆空间,通过str可以操作该实例,那么str就是StringBuffer实例的强引用
步骤2:StringBuilder str1 = str;
此时str1指向str指向的对象,同时在局部变量表上会分配空间存放str1变量。此时,该StringBuffer示例就有两个引用。对引用的"=="操作用于表示两操作数所指向的堆空间地址是否相同,不表示两操作数所指向的对象是否相等
这两个引用都是强引用
- 软引用--- 可被回收的引用
- 能力:比强引用弱一点的,软可触及的引用类型;
- 能力特点:
- 堆空间不足时,会被回收;
- 使用java.lang.SoftReference类实现;
- 示例1:软引用在系统堆内存不足时被回收
public class SoftRef {
//声明User类
public static class User {
public User(intid,String name){
this.id =id;
this.name =name;
}
public intid;
public Stringname;
@Override
public String toString() {
return"User [id=" + id + ", name=" +name + "]";
}
}
public static void main(String[]args) {
Useru = new User(1,"geym");//建立user实例,强引用
SoftReference<User>userSoftRef = new SoftReference<SoftRef.User>(u);//通过强引用,建立软引用
u =null;//去除强引用
System.out.println(userSoftRef.get());//从软引用获取强引用对象
System.gc();//垃圾回收
System.out.println("After GC:");
System.out.println(userSoftRef.get());//再次获取软引用中的对象
byte[]b =new byte[1024*925*7];//分配一块大内存,让系统认为内存紧张
System.gc();//垃圾回收
System.out.println(userSoftRef.get());//从软引用获取数据
}
}
运行参数:-Xmx10m
运行效果:
User [id=1, name=geym](第一次从软引用中获取数据)
After GC:
User [id=1, name=geym](GC没有清除软引用)
null(由于内存紧张,软引用被清除)
- 示例2:每个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过该队列,可跟踪对象的回收情况
public class SoftRefQ {
public static class User{
public User(intid,String name){
this.id =id;
this.name =name;
}
public intid;
public Stringname;
@Override
public String toString() {
return"User [id=" + id + ", name=" +name + "]";
}
}
static ReferenceQueue<User>softQueue = null;
public static class CheckRefQueueextends Thread{
@Override
public void run() {
//跟踪引用队列
while(true){
if(softQueue !=null){
UserSoftReferenceobj = null;
try{
obj = (UserSoftReference)softQueue.remove();
}catch(Exceptione){
e.printStackTrace();
}
if(obj !=null){
System.out.println("user id" +obj.uid+" is delete");
}
}
}
}
}
//实现自定义软引用类,扩展软引用的目的是记录User.uid,在后续引用队列中,可以通过这个uid知道哪个User实例被回收了
public static class UserSoftReferenceextends SoftReference<User>{
intuid;
public UserSoftReference(Userreferent,ReferenceQueue<? super User> q) {
super(referent,q);
uid=referent.id;
}
}
public static void main(String[]args) throws InterruptedException {
Threadt = new CheckRefQueue();
t.setDaemon(true);
t.start();
Useru = new User(1,"geym");//建立user实例,强引用
softQueue =new ReferenceQueue<User>();//通过强引用,建立软引用
//创建软引用时,指定了一个软引用队列,当给定的对象实例被回收时,就会被加入这个引用队列,通过访问该队列可以跟踪对象的回收情况
UserSoftReferenceuserSoftRef = new UserSoftReference(u,softQueue);
u =null;//去除强引用
System.out.println(userSoftRef.get());//从软引用获取强引用对象
System.gc();//垃圾回收
//内存足够,不会被回收
System.out.println("After GC:");
System.out.println(userSoftRef.get());//再次获取软引用中的对象
System.out.println("try to create byte array and GC");
byte[]b =new byte[1024*925*7];//分配一块大内存,让系统认为内存紧张
System.gc();//垃圾回收
System.out.println(userSoftRef.get());//从软引用获取数据
Thread.sleep(1000);
}
}
使用如下参数执行此段代码:-Xmx10m
运行结果:
User [id=1, name=geym](第一次从软引用获得对象)
After GC:
User [id=1, name=geym](GC后,软引用对象没有回收)
try to create byte array and GC(创建大数组,耗尽内存)
user id1 is delete(引用队列探测到对象被删除)
null(对象已被回收,无法再通过软引用获取对象)
- 弱引用---发现即回收
- 能力:弱可触及的引用类型。比软引用稍弱。只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。
- 能力特点:
- 垃圾回收器线程优先级很低,因此,并不一定能很快发现持有弱引用的对象。这种情况下,弱引用对象可以存在较长的时间;
- 一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中;(这一点和软引用一样,可查看软引用的代码示例)
- 使用java.lang.ref.WeakReference类实现
- 能力分析:
- 软引用,弱引用都适合来保存那些可有可无的缓存数据。这样做,可以让系统内存不足时,让这些缓存数据被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用;
- 示例1:
public class WeakRef {
public static class User{
public User(intid,String name){
this.id =id;
this.name =name;
}
public intid;
public Stringname;
@Override
public String toString() {
return"User [id=" + id + ", name=" +name + "]";
}
}
public static void main(String[]args) {
Useru = new User(1,"hey");
//构造弱引用
WeakReference<User>userWeakRef = new WeakReference<User>(u);
u =null;//去除强引用
System.out.println(userWeakRef.get());//从弱引用中重新获取对象
System.gc();//GC
//不管当前内存空间足够与否,都会回收它的内存
System.out.println("After GC:");
System.out.println(userWeakRef.get());//重新尝试从弱引用中获取对象
}
}
运行结果:
User [id=1, name=hey]
After GC:
null
- 虚引用--- 对象回收跟踪
- 能力:虚可触及的引用了类型。所有应用类型中最弱的一个。持有虚引用的对象,和没有引用几乎是一样的。随时都可能被垃圾回收器回收。
- 能力特点:
- 试图通过虚引用的get()方法取得强引用时,总是会失败;
- 虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程;
- 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,已通知应用程序对象的回收情况;
- 示例1:
public class TraceCanReliveObj {
public static TraceCanReliveObjobj;
static ReferenceQueue<TraceCanReliveObj>phantomQueue = null;
public static class CheckRefQueueextends Thread{
@Override
public void run() {
//跟踪引用队列
while(true){
if(phantomQueue !=null){
PhantomReference<TraceCanReliveObj>objt = null;
try{
objt = (PhantomReference<TraceCanReliveObj>)phantomQueue.remove();
}catch(Exceptione){
e.printStackTrace();
}
if(objt !=null){
System.out.println("TraceCanReliveObj is delete by GC");
}
}
}
}
}
@Override
protected void finalize()throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj =this;
}
@Override
public String toString() {
return"I am CanReliveObj";
}
public static void main(String[]args) throws InterruptedException {
Threadt = new CheckRefQueue();
t.setDaemon(true);
t.start();
phantomQueue =new ReferenceQueue<TraceCanReliveObj>();
obj =new TraceCanReliveObj();
PhantomReference<TraceCanReliveObj>phantomRef = new PhantomReference<TraceCanReliveObj>(obj,phantomQueue);
obj =null;
System.gc();
Thread.sleep(1000);
if(obj==null){
System.out.println("obj是null");
}else{
System.out.println("obj可用");
}
System.out.println("第2次GC");
obj =null;
System.gc();
Thread.sleep(1000);
if(obj ==null){
System.out.println("obj是null");
}else{
System.out.println("obj可用");
}
}
}
执行效果:
CanReliveObj finalize called(对象复活)
obj可用
第2次GC(第2次,对象无法复活)
TraceCanReliveObj is delete by GC(引用队列捕获到对象被回收)
obj是null
- Java虚拟机--判断可触及性(七)
- Java垃圾回收--判断可触及性
- java虚拟机之引用和可触及性的强度
- jvm可触及性
- Java虚拟机(七)
- Java虚拟机(七)
- JAVA虚拟机系列(七)
- Java虚拟机(七)虚拟机字节码执行引擎
- java判断星期(可输入日期)
- 深入理解java虚拟机(七)类加载的时机
- Java虚拟机(七)-JVM内存模型总结
- 深入理解Java虚拟机----(七)字节码执行引擎
- 深入java虚拟机(七)--Javac编译与JIT编译
- 触及软件架构(个人随笔)
- 判断闰年(七)
- C陷阱与缺陷(七)可移植性缺陷
- 深入理解Java虚拟机读书笔记七
- 深入java虚拟机(七)深入源码看java类加载器ClassLoader
- 天天写业务代码,如何成为技术大牛。 do more,do better, do exercise
- 虚拟机不能ping主机的问题
- 重要基础知识
- 哈希算法在模式匹配问题中的应用
- CentOS下的IPV6地址配置
- Java虚拟机--判断可触及性(七)
- Python 在 windows 下安装scipy
- (转)关于jsp页面中的pageEncoding和contentType以及html中的<meta标签中字符集的设置
- load pre_trained model in torch
- 【解释】while(~scanf("%d", &n))的~的含义~scanf
- ReactNative 导航栏Navigator的使用及参数navigator的传递
- 1032. 挖掘机技术哪家强(20)
- ObjectAnimator和ValueAnimator常用的java实例化方法
- Java虚拟机--垃圾回收器(八)