logo头像

Always believe youself.

JVM-3

直接内存(Direct Memory)

就是操作系统的内存,不是jvm 的内存管理。

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

为什么在读写大文件时 性能高

系统内存java 代码不能运行
fKqj3T.md.png

减少缓冲区复制操作

fKvdyT.md.png

direct memory :系统和java 代码都可以运行,共享内存。

直接内存也会有内存溢出问题

  • 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory方法
  • ByteBuffer 的实现类内部,使用 cleaner(虚引用)来监测 ByteBuffer 对象,一旦 byteBuffer 对垃圾回收,那么 会由 其他线程通过Cleaner的clean 方法 调用 freememory 来释放直接内存。

    多次 调用 byteBuffer = ByteBuffer.allocateDirext(_100Mb) 会造成直接内存溢出。

    不受jvm 管理,但是 byteBuffer 会有内存释放。

    原理: Unsafe 是 ByteBffer 底层的分配和释放内存 相关的类型,java 底层的类,可以进行释放直接内存,不建议程序员直接使用。 JDK 内部实现的,可以用反射获取 Unsafe对象 进行 分配(allocateMemory)和 释放(freeMemory)直接内存,都是主动调用。涉及一个虚引用对象,cleaner 起一个其他线程去主动调用释放内存。

    垃圾回收 只会用于java 的无用的对象的释放回收资源。

-XX:+DisableExplicitGC 禁用显式的垃圾回收 即 代码中 System.GC() 是无效的。 这个是 full GC 会回收很多部分的垃圾内存,对垃圾回收有影响。

如果设置了这个,对直接内存的回收是有影响的。但是打开了又有垃圾回收对性能有影响了。

可以推荐:使用 Unsafe 调用 freeMemory 来手动管理直接内存。

垃圾回收

堆有垃圾回收的机制

结合三个垃圾回收算法,协调工作的。

如何判断对象可以回收

  • 引用计数法:引用 +1 不引用 -1 计数为0 时进行回收。 重要弊端循环引用: 引用计数不能归0 ,所以不能被垃圾回收,造成内存泄漏。 python 引用的。

  • 可达性分析算法: (java) 确定一系列的根对象(肯定不会被当作垃圾的对象:根对象),在垃圾回收之前对堆内存中所有的对象进行扫描一遍,判断对象是否对根内存进行直接或间接的引用。反之 则被回收。

    • 哪些对象可以作为 GC Root:

fMPBgs.md.png

系统类,本地方法(操作系统方法),活动线程,加锁的对象。都是跟对象,不会被回收。

image.png

  • 四种引用
    • 强引用: 一般情况下,对象引用都是强引用, 若 A1对象 不被 B C 根对象引用时就会被 GC
    • 软引用:当A2 去掉 B 对象的强引用时,只有 C对象的软引用,他就有可能被GC (进行垃圾回收时,且内存不够时) 可配合引用队列使用。
    • 弱引用:当A3 去掉 B 对象的强引用时,只有 C对象的弱引用,他就有可能被GC (进行垃圾回收时)
    • 虚引用:必须配合引用队列使用。在虚引用引得对象在垃圾回收时,虚引用自己就会被放入到 引用队列,然后被一个hander 进行 虚引用调用的方法去释放内存。 虚引用的典型用法:ByteBuffer 直接内存的释放。
    • 终结器引用:必须配合引用队列使用。finallize 终结方法。效率低,先入队,执行的线程优先级很低,被执行的机会少,迟迟不执行,迟迟不释放内存。不建议使用。

image.png

image.png

垃圾回收算法

  • 标记清除:

    1. 标记垃圾。扫描整个堆对象,是否被 GC root (根对象)引用,是的被保留,不是则回收。
    2. 清除垃圾,释放内存空间。释放:不是内存字节的清零操作,而是把对象占用内存 中的起始结束位置记录下来,放入到空闲的地址列表里,下次分配就会进行内存分配。
    • 优点:无额外的操作,速度快
    • 缺点:会产生空间垃圾碎片。如果内存空间不连续,不能放下某个大的对象。
  • 标记整理

    1. 标记垃圾。
    2. 整理。 为了避免有内存空间碎片,会移动有效的内存空间。
    • 优点:无内存碎片
    • 缺点:速度较慢。因为有对象的移动操作, 效率低。 对象位置移动后有变化,所以涉及要改动的地方比较多。
  • 复制
    内存区划分相等的一个区域,
    1. 标记标记。
    2. 把有效的对象内存区域复制到新的内存空间去。会有空间整理,不会有空间碎片。把旧的全部清空,并交换新得内存空间和旧的空间位置。
    • 优点:无内存碎片
    • 缺点:占用双倍的内存空间。

分代垃圾回收

把堆内存大的区域划分为新生代,老年代。

  • 新生代:用完就可以丢弃的,垃圾回收频繁。 (复制算法)
  • 老年代:需长时间使用的,长时间存活的,垃圾回收很久发生一次(耗时也会长一些)。(标记的算法)

新生代的gc过程:

第一次:Minor GC(新生代空间不足时触发)
新分配对象 -> 伊甸园 (放不下) -> 幸存区 to (对象标记 +1 )-> 和幸存区 From 互换位置 -> 清理 伊甸园
第二次:
新分配对象 -> 伊甸园 (放不下) -> 幸存区 to (对象标记 +1 )-> 清理 伊甸园
      (同时)幸存区From -> 幸存区 to (对象标记 +2 )-> 和幸存区 From 互换位置  -> 清理 伊甸园 -> 清理 幸存区 to

新生代多次垃圾回收后,新生代会晋升到老年代(超过某个阈值:15)。 大对象可以直接晋升为老年代(新生代放不下,判断后直接晋升)不会触发gc。

新生代,老年代都满了 触发 full GC。(也肯定是新生代满了触发的),触发full gc 时也会先进行一次 minor GC,再抢救一次。

gc 会暂停其他用户的线程,垃圾回收结束后才会恢复,时间较短。

新生代,老年代 内存满了后 full gc后空间还是不足就会有outMemory 的错误。

5D025B13-5B2F-4A36-BECE-99232909C30F.jpg

相关VM 参数

image.png

用户线程内 内存溢出,不会让主线程 java 进程停止。

垃圾回收器

image.png

image.png

image.png

并行,并行的垃圾回收,快速执行完垃圾回收操作。不允许用户线程操作。

image.png

用户一边用,一边垃圾回收着。并发,在整个过程中会有标记工作,只有两个点会进行回收,需要 STW ,其他时间段允许用户线程操作。

image.png

垃圾回收优化

  • 调优领域

    • 内存
    • 锁竞争
    • cpu 占用
    • io
  • 确定目标

    • 【低延迟】 还是 【高吞吐量】 ,选择合适的回收器
    • CMS, G1 ,ZGC
    • ParallelGC
    • Zing
  • 最快的 GC: 是不发生 GC
  • 新生代调优: 预设堆内存,新生代调优,新生代空间也不是越大越好(目标还是要尽可能的大),峰值后会下降,因为垃圾回收的时间会长。
    • 理想的新生代空间: 【并发量 * (请求-响应)】
    • 幸存区 大到 能保留 【当前活跃对象 + 需要晋升对象】
    • 晋升阀值配置合适,让长时间存活对象尽快的晋升
  • 老年代调优:
    • 老年代的内存设置的越大越好
    • 先尝试不做调试,如果没 Full gc,则先进行调试新生代
    • 观察 Full GC 时老年代的空间占用,进行预设空间调大