java面试进阶26:如何监控和诊断JVM堆内和堆外内存使用?

堆内空间:

堆内分析工具:图形化工具,如 JConsole、VisualVM(jmc,jconsole在linux上用不了的问题,其实1可以远程连接,2可以使用xshell);命令行工具进行运行时查询,如 jstat 和 jmap 等工具;Tomcat、Weblogic内置功能;GC 日志

1、新生代:内部又分为 Eden 区域,作为对象初始分配的区域;两个 Survivor,有时候也叫 from、to 区域,被用来放置从 Minor GC 中保留下来的对象。JVM 会随意选取一个 Survivor 区域作为“to”,然后会在 GC 过程中进行区域间拷贝,也就是将 Eden 中存活下来的对象和 from 区域的对象,拷贝到这个“to”区域。这种设计主要是为了防止内存的碎片化,并进一步清理无用对象。

Hotspot JVM 还有一个概念叫做 Thread Local Allocation Buffer(TLAB)TLAB 仍然在堆上,它是分配在 Eden 区域内的。其内部结构比较直观易懂,start、end 就是起始地址,top(指针)则表示已经分配到哪里了。所以我们分配新对象,JVM 就会移动 top,当 top 和 end 相遇时,即表示该缓存已满,JVM 会试图再从 Eden 里分配一块儿。为了避免多线程同时分配内存时操作同一地址。

2、老年代:放置长生命周期的对象,通常都是从 Survivor 区域拷贝过来的对象。如果对象较大,JVM 会试图直接分配在 Eden 其他位置上;如果对象太大,完全无法在新生代找到足够长的连续空闲空间,JVM 就会直接分配到老年代。

3. 永久代:储存 Java 类元数据、常量池、Intern 字符串缓存,在 JDK 8 之后就不存在永久代这块儿了。

参数配置:

最大堆体积-Xmx value     初始的最小堆体积 -Xms value 

老年代和新生代的比例 -XX:NewRatio=value,默认情况下,这个数值是 2,意味着老年代是新生代的 2 倍大;换句话说,新生代是堆大小的 1/3。新生代的大小-XX:NewSize=value

Survivor 区域就是 Eden 的 1/8 大小,也就是新生代的 1/10,因为 YoungGen=Eden + 2*Survivor,JVM 参数格式是-XX:SurvivorRatio=value

如果 Xms 小于 Xmx,堆的大小并不会直接扩展到其上限,也就是说保留的空间(reserved)大于实际能够使用的空间(committed)。当内存需求不断增长的时候,JVM 会逐渐扩展新生代等区域的大小,所以 Virtual 区域代表的就是暂时不可用(uncommitted)的空间。

堆外空间:

堆外 NMT 特性对 JVM 进行分析,会打印如下图信息


依次打印

Java 堆、

 Class 内存占用,它所统计的就是 Java 类元数据所占用的空间、

 Thread,这里既包括 Java 线程,如程序主线程、Cleaner 线程等,也包括 GC 等本地线程、(JDK 9 的默认 GC 是 G1,虽然它在较大堆场景表现良好,但本身就会比传统的 Parallel GC 或者 Serial GC 之类复杂太多,所以要么降低其并行线程数目,要么直接切换 GC 类型;)

Code 统计信息,显然这是 CodeCache 相关内存,关闭JIT compiler的TieredCompilation,使用情况下降

GC 部分了

Compiler 部分,就是 JIT 的开销

Internal(JDK 11 以后在 Other 部分)部分,其统计信息包含着 Direct Buffer 的直接内存,这其实是堆外内存中比较敏感的部分,很多堆外内存 OOM 就发生在这里

 

程序的方式而不是工具,对 Java 内存使用进行监控,有哪些技术可以做到?      java.lang.Runtime类有freeMemory()、totalMemory()等方法可以获取到jvm内存情况,看了一下是本地方法。

为什么在标记垃圾的时候,需要stop the world?是为了避免在标记的时候又有对象在堆内生成,如果这个对象对其他未标记对象有引用,而这个时候由于gc而清理掉了未标记的对象,会有问题,不过以cms为例,它有不同的mark: initial mark,conc mark, remark;conc时候不需要stw;其他需要短暂stw,这样引用关系才不变,另外效率也高

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章