对于堆内内存的监控
JVM提供了一些图形化的工具 JConsole VisualVM 可以直接连接到Java进程,在图形化中掌握内存的情况
JConsole为例,内存显示也分为了堆内存 堆外部分
jstat jmap等工具,提供了一些选项,可以查看堆 方法区等使用数据
使用jmap等提供命令,生成堆转储的文件
使用的是Tomcat,可以直接查看内存管理
这个问题,需要我们清晰的对JVM区域有个认知
以及对于特定区域,该使用上面工具去查看
对于收集工具,有JConsole 和 JMC
JMC可以配合JMX进行普通的管理
还可以配合JFR技术,以一种很低开销的,收集和分析JVM底层的Profiling 和 事件等信息
那么,我们先看下对内部的结构
之前版本的GC的年代区分如上
1.新生代
新生代是对象创建和销毁的对象
里面分为Eden区和Survivor区域,在GC过程中,会进行区域的拷贝,同时整理内存
然后就是TLAB的概念,就是为每一个线程预先分配一个私有区域,避免线程保存变量的时候,需要请求分配,这可能导致出现锁机制,关于这个分配,是在Eden区域的
分配流程如上,就是start end是固定的,top指向现在分配的地方了,每当分配一些,就移动top
top到头了,end就会在分配一块
老年代,就是存储长时间存活或者大对象的地方
永久代
存储Java类对象 常量池 Intern字符串缓存的地方
在Java 8之后,已经无了
对于常见的JVM参数有
-Xmx
设置最大堆体积
-XMS
设置最小堆空间
-XX:NewRatio=value
老年代和新生代的比例
默认比例是1:2
新生代占有堆空间的1/3
-XX:NewSize=value
直接设置内存大小
-XX:SurvivorRatio=value
Eden和Survivor的大小比例,默认是1/8 两个Survivor的区域相加和Eden就是 2:8
而且,JVM并不会直接将堆的大小扩展至Xmx的上限,而是等待内存有需求的时候,恢复到设置的上限
然后,我们看下JVM对外内存包含上面
对于JMC或者JConsole的管理界面,会统计非堆的内存,下面是JMC的截图
这就统计了非堆内存的信息
然后我们开启NMT(堆外空间存储统计),进行打印统计内存
-XX:NativeMemoryTracking=summary
并且在应用退出的时候,打印NMT统计信息
-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics
运行了一个HellpWorld,在程序结束的时候,打印了对应的NMT统计信息
第一部分是Java对
然后是Class的内存占用,元数据空间
可以利用下面的参数进行调整大小
-XX:MaxMetaspaceSize=value
然后就是可以调整类加载器元数据区大小
如下
-XX:InitialBootClassLoaderMetaspaceSize=30720
然后是线程,有足足25个
里面有Java线程 程序主线程 Cleaner线程 GC等本地线程
这是因为JDK9给出的默认GC是G1,对于高并发的场景有效,但是较为复杂
所以我们可以替换为单线程的GC
-XX:+UseParallelGC
下面是替换了默认GC,关闭了
进一步减少线程,就是可以关闭JVM的动态编译
JIT的TieredCompilation
使得本地线程变少
-XX:TieredCompilation
然后是CodeCache的相关内存
-XX:InitialCodeCacheSize=value
设置这部分的初始大小
-XX:ReservedCodeCacheSize=value
所以,减少线程和内存的开销,可以考虑替换对应的垃圾收集器
考虑关闭JIT
减少Direct Buffer的直接内使用