从finalize的执行来看强引用、弱引用、软引用

finalize的执行来看什么是GC Root我们测试了不同GC Root可达时,对对象finalize方法的执行的影响。下面我们测试一下不同引用对对象finalize方法的执行的影响。

public class RootTest {
    static int gcTime = 0;

    public static void main(String[] args) throws InterruptedException {

        SoftReference softReference = new SoftReference<>(new UnusedLock("soft reference"));
        WeakReference weakReference = new WeakReference<>(new UnusedLock("weak reference"));
        new UnusedLock("no reference");
        UnusedLock unusedLock = new UnusedLock("strong reference");

        new Thread() {
            @Override
            public void run() {
                int x = 0;
                synchronized (new UsedLock()) {
                    while (x < 3) {
                        System.out.println("Thread run " + ++x);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread run over");
                }
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread finalize() " + Thread.currentThread().getName());
            }
        }.start();

        gc();
        Thread.sleep(5000);
        gc();
        Thread.sleep(5000);
    }

    static void gc() {
        System.out.println("gc start " + ++gcTime);
        System.gc();
        System.out.println("gc over " + gcTime);
    }
}


class UsedLock {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UsedLock finalize()");
    }
}

class UnusedLock {
    private String sign = null;

    public UnusedLock(String sign) {
        this.sign = sign;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UnusedLock finalize(): " + sign);
    }
}

我们分别使用软引用、弱引用、强引用以及不引用来测试对象finalize方法的执行

运行结果:

Thread run 1
gc start 1
UnusedLock finalize(): no reference
UnusedLock finalize(): weak reference
gc over 1
Thread run 2
Thread run 3
Thread run over
gc start 2
gc over 2
UsedLock finalize()
Thread finalize() Finalizer

可见,第一次执行gc时,没有被引用的对象和弱引用的对象finalze方法被调用,软引用和强引用的对象finalze方法没有被调用。

第二次执行gc时,软引用和强引用的对象finalze方法也没有被调用。

我们来模拟一下内存不足时的情况,代码如下:

public class RootTest {
    static int gcTime = 0;

    public static void main(String[] args) throws InterruptedException {

        SoftReference softReference = new SoftReference<>(new UnusedLock("soft reference"));
        WeakReference weakReference = new WeakReference<>(new UnusedLock("weak reference"));
        new UnusedLock("no reference");
        UnusedLock unusedLock = new UnusedLock("strong reference");
        byte[] bytes = new byte[1024 * 1024 * 1024];
        System.out.println(bytes.length);
        bytes = new byte[1024 * 1024 * 1024];
        System.out.println(bytes.length);

        new Thread() {
            @Override
            public void run() {
                int x = 0;
                synchronized (new UsedLock()) {
                    while (x < 3) {
                        System.out.println("Thread run " + ++x);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread run over");
                }
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread finalize() " + Thread.currentThread().getName());
            }
        }.start();

//        gc();
//        Thread.sleep(5000);
//        gc();
//        Thread.sleep(5000);
    }

    static void gc() {
        System.out.println("gc start " + ++gcTime);
        System.gc();
        System.out.println("gc over " + gcTime);
    }
}


class UsedLock {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UsedLock finalize()");
    }
}

class UnusedLock {
    private String sign = null;

    public UnusedLock(String sign) {
        this.sign = sign;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UnusedLock finalize(): " + sign);
    }
}

我们不再主动调用gc了,直接分两次申请1024 * 1024 * 1024 byte的内存,看下运行结果:

1073741824
UnusedLock finalize(): soft reference
UnusedLock finalize(): no reference
UnusedLock finalize(): weak reference
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at at.miao.java_lib.RootTest.main(RootTest.java:17)

可见,第一次申请成功了,但是第二次申请的时候内存不足了,这时候软引用、无引用、弱引用的对象finalize方法都被调用了,对象被回收后剩余内存还是不足,造成了OutOfMemoryError。

软引用会在内存不足时被GC回收。

<think>好的,用户想了解在Java中如何打印某个对象被哪个方法引用的调试技巧。首先,我需要回忆Java中对象引用追踪的方法。常见的办法包括使用调试工具或分析堆栈跟踪,但用户可能想要更直接的代码级解决方案。 记得Java的弱引用和引用队列(ReferenceQueue)可以用来追踪对象的回收情况,但可能无法直接显示哪个方法引用了对象。另一个思路是使用Java Management Extensions(JMX)和内存分析工具,比如jmap和jhat,但这些可能需要外部工具,用户可能希望用代码实现。 接下来,想到Java的Instrumentation API,它允许在类加载时添加字节码增强,从而跟踪对象的创建和引用。不过,这需要编写Java代理,可能比较复杂。用户可能需要更简单的方法,比如在代码中插入日志,记录对象被传递的地方。 但用户的问题更偏向于运行时查找哪个方法持有引用。此时,可能需要检查堆栈跟踪。例如,可以在对象的方法中打印当前线程的堆栈,但这只能显示当前执行路径,无法追踪所有引用。 突然想到,使用Java的反射机制可能无法直接获取对象的引用者,因为Java没有内置的API来反向查找引用。因此,可能需要借助分析工具,比如VisualVM或Eclipse MAT,这些工具可以生成堆转储并分析对象引用链。 不过用户提到“打印”某个对象被哪个方法引用,可能希望在代码中实现。这时候可能需要使用Java的调试接口,如JDI(Java Debug Interface),但这通常用于调试器,不是常规代码的一部分。 综合来看,直接通过编程方式在Java中打印对象被哪个方法引用较为困难,通常推荐使用堆转储分析工具。但可以尝试在对象的finalize()方法中输出信息,当对象被回收时,可能提示没有强引用存在,但无法确切指出引用者。 另外,参考引用内容中的[^2]提到线程锁定的资源,可能通过线程堆栈跟踪来查找对象被哪个线程持有,但仅限于同步块的情况。可以使用Thread.getAllStackTraces()获取所有线程的堆栈,检查哪些方法正在操作该对象。 总结可能的步骤:1. 生成堆转储并用MAT分析;2. 在代码中插入日志,记录对象传递;3. 使用finalize方法检测对象回收;4. 检查线程堆栈跟踪。需要根据用户的具体场景选择合适的方法,并可能需要结合外部工具。</think>在Java中,要追踪对象被哪个方法引用,可以通过以下方法实现: ### 方法一:使用堆转储分析 1. 生成堆转储文件: ```bash jmap -dump:live,format=b,file=heapdump.hprof <pid> ``` 2. 使用Eclipse Memory Analyzer (MAT)分析: - 打开`heapdump.hprof`文件 - 查找目标对象 - 右键选择"Merge Shortest Paths to GC Roots" - 查看完整的引用链,包含类和方法信息 ### 方法二:运行时堆栈追踪(针对同步对象) ```java public static void printLockHolders(Object target) { for (Thread t : Thread.getAllStackTraces().keySet()) { if (Thread.holdsLock(target)) { // 仅适用于synchronized块 System.out.println("Lock held by: " + t.getName()); for (StackTraceElement ste : t.getStackTrace()) { System.out.println("\tat " + ste); } } } } ``` ### 方法三:使用WeakReference监听(需配合引用队列) ```java ReferenceQueue<Object> queue = new ReferenceQueue<>(); WeakReference<Object> ref = new WeakReference<>(targetObject, queue); // 定期检查队列 new Thread(() -> { while (true) { Reference<?> clearedRef = queue.remove(); System.out.println("对象被回收,最后引用来自:" + clearedRef.getClass().getName()); } }).start(); ``` ### 方法四:字节码增强(需Java Agent) 1. 创建Java Agent: ```java public class TrackingAgent { public static void premain(String args, Instrumentation inst) { inst.addTransformer((loader, className, classBeingRedefined, protectionDomain, classfileBuffer) -> { // 插入对象访问追踪字节码 return transformBytecode(classfileBuffer); }); } } ``` 2. 在MANIFEST.MF中声明: ``` Premain-Class: TrackingAgent ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值