JAVA通过自带的垃圾回收机制来管理内存,但对于项目的内存分析也要了解,比如项目的内存使用情况,何时回收?或出现内存泄露如何排查?
首先看看JVM内存分配图:
如上图,jvm包含了堆、本地方法栈,
堆上存放新建(New)的对象,它被划为为新生代,老年代两个区域,新生代又分为伊甸园、幸存者两个区域,不同的区域存放的对象生命周期不同
说说JAVA中几种内存溢出的情况:
1,JVM堆溢出(java.lang.OutOfMemoryError: java heap space)
当生成一个新对象,JVM内存申请如下流程:
1),jvm先尝试在eden分配新对象所需的内存,若内存足够,则将对象放入eden返回
2),若内存不够,jam启动youngGC,试图将eden不活跃的对象释放掉,若释放后仍不足以分配内存,则将Eden活跃的对象放入survivor中。
3),survivor作为Eden和old的中间交换区域,若old空间足够,survivor去对象会被移动到old区,否则留在survivor区
4),当old区不够时,jvm会在old区进行fullGC,若fullGC后,survivor和old仍然无法存放从Eden复制过来的对象,则会出现“outOfMemoryError: java heap space”
解决方法:加大堆内存的大小,通过设置-Xms(java heap初始化大小,默认是物理内存1/64) -Xmx(java heap的最大值) -Xmn(新生代heap的大小,一般为Xmx3或4分之一,注:增加新生代后会减少老年代的大小)
2,方法区内存溢出(java.lang.OutMemoryError: permGen space)
方法区主要是用来存放类信息,常量、静态变量等,所以程序中类加载过多(引入第三方包),或者过多使用反射、cglib这种动态代理,就可能导致该区域内存溢出
解决方法:通过设置-XX:PermSize(内存永久区初始值)和-XX:MaxPerSize(内存永久区最大值)的大小
3,线程栈溢出(java.lang.StackOverflowError)
线程栈是线程独有的一块内存区域,所以线程栈溢出必定是线程运行是出现错误,一般是递归太深,或者方法层级调用太深引起的
解决方法:设置栈区的大小,通常栈的大小是1-2M,可通过-Xss设置线程的栈的大小,jdK5以后每个栈默认大小为1M。
下面针对线上的某个项目,查看它是否出现内存溢出的情况
1,如何查看项目的内存使用情况?
jmap -heap pid
Attaching to process ID 64909, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.91-b14 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 2147483648 (2048.0MB) NewSize = 44564480 (42.5MB) MaxNewSize = 715653120 (682.5MB) OldSize = 89653248 (85.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 31457280 (30.0MB) used = 12660000 (12.073516845703125MB) free = 18797280 (17.926483154296875MB) 40.24505615234375% used From Space: capacity = 1572864 (1.5MB) used = 1179648 (1.125MB) free = 393216 (0.375MB) 75.0% used To Space: capacity = 9437184 (9.0MB) used = 0 (0.0MB) free = 9437184 (9.0MB) 0.0% used PS Old Generation capacity = 40894464 (39.0MB) used = 4514768 (4.3056182861328125MB) free = 36379696 (34.69438171386719MB) 11.040046887520033% used
查看Java内存的分配情况及新生代、老年代内存的使用情况,确定是否内存分配过小?
2,判断是否出现内存泄露
jstat -gcutil pid 2000
上面命令表示每隔2秒打印出GC的使用回收情况,若FGC很多很可能出现了内存泄露
3,查看占用内存最多的对象
jmap -histo:live pid | more
按使用大小进行了排序,重点查看排在前面的对象,看是否程序写的有问题。
另外可以将jvm的堆内存导出来分析,使用
jmap -dump:format=b,file=dynamicapi.hprof pid
使用java vistual工具分析,jdk自带了jvisualvm就可以进行内存分析,执行命令:
进入jkd的bin目录,执行jvisualvm
点击文件->装入,选择堆内存文件,即可看到堆中类的使用情况