Java 堆外内存

Java 堆外内存

在 Java 虚拟机中,分配对象基本上都是在堆上进行的,然而在有些情况下,缓存的数据量非常大时,使用磁盘或者分布式缓存就会比较合适,这时堆外缓存就是一个比较合适的选择。一般会认为 Java 进程启动后,除了分配的堆(heap)内存之外的内存都为堆外内存。堆外内存在没有引用时,也会被 Java 垃圾收集器进行回收。

在这里插入图片描述

如上图所示,堆外内存就是 Heap Out Memory 部分,在 Java 虚拟机中由 DirectByteBuffer 对象表示。

堆外内存的分配方式

Java 分配堆外内存的方式有两种方式,一种使用Unsafe类来进行分配,另一种使用ByteBuffer来进行分配。

使用 Unsafe 类进行分配

需要注意的是,使用 Unsafe 来进行分配,不受 -XX:MaxDirectMemorySize 参数的限制。

private static final long SIZE = 40 * 1024 * 1024;

/**
 * 获取 Unsafe 实例,通过反射的方式来获取,避免触发 SecurityException
 *
 * 通过反射的方式,堆外内存不受 -XX:MaxDirectMemorySize 参数限制
 */
private static Unsafe getUnsafeInstance() {
    try {
        Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        return unsafe;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

/**
 * 分配堆外内存方式 1
 */
private static void allocateOffHeapMemory1() {
    Unsafe unsafe = getUnsafeInstance();
    // 分配内存
    long address = unsafe.allocateMemory(SIZE);

    // 重新分配内存,把数据从
    // unsafe.reallocateMemory(address, SIZE);

    // 释放内存
    // unsafe.freeMemory(address);
}

使用 ByteBuffer 进行分配

private static final long SIZE = 40 * 1024 * 1024;

/**
 * 分配堆外内存方式 2
 *
 * 受 -XX:MaxDirectMemorySize 参数限制
 */
private static void allocateOffHeapMemory2() {
    ByteBuffer buffer = ByteBuffer.allocateDirect((int) SIZE);
    // ...
}

堆外内存的查看方式

想要查看一个 Java 进程的堆外内存的信息,可以通过如下命令来启动该 Java 进程。

java -XX:+DisableExplicitGC -XX:MaxDirectMemorySize=40M -XX:NativeMemoryTracking=summary -Xms300M -Xmx300M -classpath [your class]
  • -XX:+DisableExplicitGC:用来显示禁止垃圾收集器回收。
  • -XX:MaxDirectMemorySize=40M:设置直接内存大小为 40M,如果使用 ByteBuffer 来执行分配时,超过 40M 会抛出 OOM 异常。
  • -XX:NativeMemoryTracking=summary:配置查看堆外内存的跟踪。
  • -Xms300M -Xmx300M:将堆内存限定在 300M。

当你的 Java 进程运行起来之后,可以通过下面的命令来查看虚拟机中内存的实际分配。

其中 reserved 表示应用可以使用的内存大小,committed 表示正在使用的内存大小。

# 通过 jps 找出对应进程的 pid
jps

# 使用 jcmd 查看汇总信息
jcmd [your process pid] VM.native_memory summary scale=MB

# 输出
Native Memory Tracking:

Total: reserved=1746MB, committed=478MB
-                 Java Heap (reserved=300MB, committed=300MB)
                            (mmap: reserved=300MB, committed=300MB) 
 
-                     Class (reserved=1046MB, committed=19MB)
                            (classes #470)
                            (malloc=14MB #179) 
                            (mmap: reserved=1032MB, committed=5MB) 
 
-                    Thread (reserved=33MB, committed=33MB)
                            (thread #34)
                            (stack: reserved=33MB, committed=33MB)
 
-                      Code (reserved=244MB, committed=3MB)
                            (mmap: reserved=244MB, committed=3MB) 
 
-                        GC (reserved=27MB, committed=27MB)
                            (malloc=16MB #144) 
                            (mmap: reserved=11MB, committed=11MB) 
 
-                  Internal (reserved=54MB, committed=54MB)
                            (malloc=54MB #1678) 
 
-                    Symbol (reserved=1MB, committed=1MB)
                            (malloc=1MB #98) 
                            (arena=1MB #1)
  • Java Heep:可以观察到堆(heap)内内存限定在 300M。
  • Class:表示已经加载 class 数量以及占用的内存。
  • Thread:表示目前有多少个线程以及占用的内存。
  • Code:表示编译器生成代码所占用的内存。
  • GC:表示垃圾回收器需要使用多少内存来完成垃圾回收动作。
  • Internal: 包含命令行解析器使用的内存、JVMTI、PerfData 以及 Unsafe 分配的内存等等,可以观察到上述代码分配的 40M 堆外内存包含此处的 54M 内。
    • JVMTI(JVM Tool Interface):是开发和监视 Java 虚拟机的编程接口。
    • PerfData:是 Java 虚拟机中用来记录一些指标数据的文件。
  • Symbol:表示字符串表、常量池等所占用的内存。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GettingReal

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值