JVM内存结构(4)

简介: JVM内存结构

JVM内存结构(3)https://developer.aliyun.com/article/1530771

5.6 StringTable垃圾回收

-Xmx18m -XX:+PrintStringTableStatistics -XX: +PrintGCDetails -verbose:gc

摄者虚拟机参数,分别是设置虚拟机堆内存的最大值,打印字符串表的统计信息,打印垃圾回收的详细信息

桶的数量, 总占用空间等信息

类名啊,方法名啊也是以字符串常量存储

添加了一万多个对象,但是只有7千多个,因为内存不足触发垃圾回收

5.7 StringTable 性能调优

底层是一个hash表,性能和大小相关

太小的话就会容易出现hash冲突

因为StringTable是由HashTable实现的,所以可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间

-XX:StringTableSize=xxxx
//最低为1009
  • 考虑是否将字符串对象入池
  • 可以通过intern方法减少重复入池,保证相同的地址在StringTable中只存储一份

6.直接内存

6-1 定义

  • 常见于NIO操作,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受JVM 内存回收影响

这里系统缓冲区复制到java缓冲区造成了不必要的复制

public class Demo1_9 {
    static final String FROM = "E:\\编程资料\\第三方教学视频\\youtube\\Getting Started with Spring Boot-sbPSjI4tt10.mp4";
    static final String TO = "E:\\a.mp4";
    static final int _1Mb = 1024 * 1024;
    public static void main(String[] args) {
        io(); // io 用时:1535.586957 1766.963399 1359.240226
        directBuffer(); // directBuffer 用时:479.295165 702.291454 562.56592
    }
    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer 用时:" + (end - start) / 1000_000.0);
    }
    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io 用时:" + (end - start) / 1000_000.0);
    }
}

这里是使用了directbuffer 之后的效果

操作系统画出来一片缓冲区,这片缓冲区java代码可以直接访问,这样少了一次缓冲区的复制操作

直接内存溢出是Direct buffer memory

6.2 分配和回收原理

  • 使用了Unsafe对象完成直接内存的分配和回收,并且回收需要主动调用freeMemory方法
  • ByteBuffer的实现类内部,使用Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过
    Cleaner的clean方法调用freeMemory来释放直接内存
/**
 * 禁用显式回收对直接内存的影响
 */
public class Demo1_26 {
    static int _1Gb = 1024 * 1024 * 1024;
    /*
     * -XX:+DisableExplicitGC 显式的
     */
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println("分配完毕...");
        System.in.read();
        System.out.println("开始释放...");
        byteBuffer = null;
        System.gc(); // 显式的垃圾回收,Full GC
        System.in.read();
    }
}

直接内存的释放不能通过垃圾回收只能通过底层的unsafe对象的freeMemory方法

/**
 * 直接内存分配的底层原理:Unsafe
 */
public class Demo1_27 {
    static int _1Gb = 1024 * 1024 * 1024;
    public static void main(String[] args) throws IOException {
        Unsafe unsafe = getUnsafe();
        // 分配内存
        long base = unsafe.allocateMemory(_1Gb);
        unsafe.setMemory(base, _1Gb, (byte) 0);
        System.in.read();
        // 释放内存
        unsafe.freeMemory(base);
        System.in.read();
    }
    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);
            return unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

当bytebuffer被回收时就会触发虚引用对象cleaner 中的create方法,然后会调用回调任务对象Delocator就会调用freeMemory方法

有时候我们会-XX:+DisableExplicitGC显式的 禁用显示的垃圾回收,因为这个很损耗性能,这样也会间接的让我们无法手动直接调用垃圾回收拉释放直接内存

这时候我们可以使用usafe对象来释放直接内存

(byte) 0);

System.in.read();

// 释放内存
    unsafe.freeMemory(base);
    System.in.read();
}
public static Unsafe getUnsafe() {
    try {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        return unsafe;
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

}

[外链图片转存中...(img-gIZ18FLN-1696576900268)]
当bytebuffer被回收时就会触发虚引用对象cleaner 中的create方法,然后会调用回调任务对象Delocator就会调用freeMemory方法
有时候我们会`-XX:+DisableExplicitGC显式的` 禁用显示的垃圾回收,因为这个很损耗性能,这样也会间接的让我们无法手动直接调用垃圾回收拉释放直接内存
这时候我们可以使用`usafe`对象来释放直接内存
相关文章
|
2天前
|
Java Windows
为什么JVM在内存返还策略上会左右为难
为什么JVM在内存返还策略上会左右为难?
|
13天前
|
存储 Java 程序员
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
37 10
|
13天前
|
存储 运维 Java
Java面试题:JVM的内存结构有哪些主要部分?请简述每个部分的作用
Java面试题:JVM的内存结构有哪些主要部分?请简述每个部分的作用
29 9
|
13天前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
31 3
|
13天前
|
监控 Java 开发者
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
23 2
|
6天前
|
存储 监控 算法
探索Java虚拟机:深入理解JVM内存模型和垃圾回收机制
在Java的世界中,JVM是核心所在,它不仅承载着代码的运行,还管理着内存资源。本文将带你深入了解JVM的内存模型和垃圾回收机制,通过具体数据与案例分析,揭示它们对Java应用性能的影响,并探讨如何优化JVM配置以提升效率。
|
8天前
|
监控 算法 Java
怎么用JDK自带工具进行JVM内存分析
JVM内存分析工具,如`jps`、`jcmd`、`jstat`、`jstack`和`jmap`,是诊断和优化Java应用的关键工具。`jps`列出Java进程,`jcmd`执行诊断任务,如查看JVM参数和线程堆栈,`jstat`监控内存和GC,`jstack`生成线程堆栈信息,而`jmap`则用于生成堆转储文件。这些工具帮助排查内存泄漏、优化内存配置、性能调优和异常分析。例如,`jmap -dump:file=heapdump.hprof <PID>`生成堆转储文件,之后可以用Eclipse Memory Analyzer (MAT)等工具分析。
|
11天前
|
存储 算法 Java
JAVA内存模型与JVM内存模型的区别
JAVA内存模型与JVM内存模型的区别
|
13天前
|
存储 缓存 安全
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
14 0
|
13天前
|
存储 设计模式 监控
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
20 0