使用Java VisualVM观察过期对象引用现象

《Effective Java》书中第7条中提到:一般来说,只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。

此次测试过程我用的jdk8,在jdk的bin目录下可以找到jvisualvm.exe,双击打开。

idea设置堆内存限制-Xmx100M -Xms100M ,如下图所示

测试代码如下:

java 复制代码
import java.util.Arrays;
import java.util.EmptyStackException;

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final  int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack(){
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e){
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop(){
        if(size == 0){
            throw new EmptyStackException();
        }
        Object element = elements[--size];
        return element;
    }

    private void ensureCapacity(){
        if(elements.length == size){
            elements = Arrays.copyOf(elements, 2 * size +1);
        }
    }
}
java 复制代码
public class ObsoleteReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        Stack stack = new Stack();
        // 压入大量大对象
        for (int i = 0; i < 80; i++) {
            stack.push(new byte[1024 * 1024]);
            System.out.println("压入第 " + i + " 个 1MB 数组");
        }
        // 全部弹出!业务上不再使用这些对象
        for (int i = 0; i < 80; i++) {
            stack.pop();
        }
        System.out.println("所有对象pop完成,业务层面不再使用");

        Thread.sleep(30000);
    }
}

运行代码后,进入Java VisualVM,在左侧找到当前代码运行的程序,双击,然后点击右侧上方的"监视",如下图所示

等待idea控制台输出 所有对象pop完成,业务层面不再使用 后点击"执行垃圾回收"按钮,如下图所示,可以看到,堆内存的使用量并没有明显下降。说明elements 数组内部仍然持有大量byte对象引用。

修改pop()方法,手动清空过期引用

typescript 复制代码
public Object pop(){
    if(size == 0){
        throw new EmptyStackException();
    }
    Object element = elements[--size];
    //手动清空过期引用
    elements[size] = null;
    return element;
}

再次运行代码,点击执行垃圾回收,观察堆使用情况,等待idea控制台输出 所有对象pop完成,业务层面不再使用 后点击"执行垃圾回收"按钮,可以看到堆使用量大幅下降,说明大量过期对象已被回收。

相关推荐
SL_staff4 小时前
告别报表重复开发:JVS-BI 模板市场的参数化继承实践
java·数据分析·数据可视化
SL_staff5 小时前
从邮箱传文档到协同流重构:企业文档协作的系统性破局实践
java·开源·github
smallyoung6 小时前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
Bazingga8 小时前
Java16 record 完整拆解——语法、语义、限制、反射
java·后端
SL_staff10 小时前
规则联动:给工厂装上‘自动化神经反射弧’——JVS·物联网平台规则引擎实战教程
java·人工智能·物联网
骑着蜗牛撵大象32710 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
WJ.Polar10 小时前
Java封装、继承、多态
java·开发语言
ly768910 小时前
PostgreSQL 备份恢复策略:pg_basebackup、WAL 归档与 PITR 在企业 Java 系统中的落地
java·数据库·postgresql·pitr·pg_basebackup·wal 归档
摇滚侠10 小时前
Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
java·开发语言
奇牙coding11 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai