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
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
26 3
|
7天前
|
存储 监控 算法
【JVM】如何定位、解决内存泄漏和溢出
【JVM】如何定位、解决内存泄漏和溢出
21 0
|
7天前
|
存储 XML 安全
JVM系列5-类文件结构
JVM系列5-类文件结构
7 0
|
7天前
|
算法 安全 Java
JVM系列4-垃圾收集器与内存分配策略(二)
JVM系列4-垃圾收集器与内存分配策略(二)
17 0
JVM系列4-垃圾收集器与内存分配策略(二)
|
7天前
|
存储 监控 算法
JVM系列4-垃圾收集器与内存分配策略(一)
JVM系列4-垃圾收集器与内存分配策略(一)
17 0
|
13天前
|
Java
JDK8中JVM堆内存划分
JDK8中JVM堆内存划分
13 0
|
13天前
|
缓存 关系型数据库 MySQL
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
30 3
|
14天前
|
Java 数据库连接 图形学
JVM内存泄漏检测与处理
JVM内存泄漏检测与处理
13 0
|
14天前
|
存储 机器学习/深度学习 Java
探索JVM 内存分配
探索JVM 内存分配
12 0
|
14天前
|
存储 Java 编译器
jvm内存结构
jvm内存结构
22 0

热门文章

最新文章