JAVA内存分析

JAVA通过自带的垃圾回收机制来管理内存,但对于项目的内存分析也要了解,比如项目的内存使用情况,何时回收?或出现内存泄露如何排查?

首先看看JVM内存分配图:

671D4492-75BB-4046-8B2E-199ABDE70734

如上图,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

D09AEF2B-2D3E-4D50-B74C-E603875EAD70

上面命令表示每隔2秒打印出GC的使用回收情况,若FGC很多很可能出现了内存泄露

3,查看占用内存最多的对象

jmap -histo:live pid | more

0B137198-E38B-4466-A554-E68FEC36EC61

按使用大小进行了排序,重点查看排在前面的对象,看是否程序写的有问题。

另外可以将jvm的堆内存导出来分析,使用

jmap -dump:format=b,file=dynamicapi.hprof pid

使用java vistual工具分析,jdk自带了jvisualvm就可以进行内存分析,执行命令:

进入jkd的bin目录,执行jvisualvm

点击文件->装入,选择堆内存文件,即可看到堆中类的使用情况

3E5ECD10-58E2-450B-A8C4-31B276689EC6

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注