JVM内存机制和垃圾回收GC

JVM内存基础

JVM堆

Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象。 对象的建立方式就是那些new一类的操作,当对象无用后,是GC来负责这个无用的对象。

分类:

JVM堆结构示意图

一个2G堆大小的JVM,可能总共占用多少内存的?

堆内存设置为2G,那一个有1000个线程的JVM可能需要:2G + 1000 * 1M + 256M + 48/240M + (~2G) = 5.5G (3.5G)

垃圾回收GC

目的:在堆中,找到已经无用的对象,并把这些对象占用的空间收回使其可以重新利用。

算法思路:

GC过程示意图

案例

# 内存监控的方法: jmap -heap pid(查看java 堆(heap)使用情况)
/usr/java/jdk1.8.0_162/bin/jmap -heap 87303 > jmap_87303.txt

# 内容如下:
Attaching to process ID 87303, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.162-b12

using thread-local object allocation.
Parallel GC with 23 thread(s)   # GC方式

Heap Configuration: # 堆内存初始化配置
   MinHeapFreeRatio         = 0 # -XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
   MaxHeapFreeRatio         = 100   # -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapSize              = 536870912 (512.0MB)   # -XX:MaxHeapSize=设置JVM堆的最大
   NewSize                  = 178782208 (170.5MB)   # -XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize               = 178782208 (170.5MB)   # -XX:MaxNewSize=设置JVM堆的‘新生代’的最大
   OldSize                  = 358088704 (341.5MB)   # -XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
   NewRatio                 = 2 # -XX:NewRatio=:‘新生代’和‘老生代’的大小比
   SurvivorRatio            = 8 # -XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比
   MetaspaceSize            = 21807104 (20.796875MB)    # -XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小(现在是jdk8)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB # -XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大(现在是jdk8)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage: # 堆内存分布
PS Young Generation
Eden Space: # Eden区内存分布
   capacity = 174587904 (166.5MB)   # Eden区总容量
   used     = 4099296 (3.909393310546875MB) # Eden区已使用
   free     = 170488608 (162.59060668945312MB)  #Eden区剩余容量
   2.3479839702984235% used # Eden区使用比率
From Space: # Survivor区(A区)的内存分布
   capacity = 2097152 (2.0MB)
   used     = 0 (0.0MB)
   free     = 2097152 (2.0MB)
   0.0% used
To Space:   # 另一个Survivor区(B区)的内存分布
   capacity = 2097152 (2.0MB)
   used     = 0 (0.0MB)
   free     = 2097152 (2.0MB)
   0.0% used
PS Old Generation   # Old区内存分布
   capacity = 358088704 (341.5MB)
   used     = 78736888 (75.08934783935547MB)
   free     = 279351816 (266.41065216064453MB)
   21.988095999811264% used

38991 interned Strings occupying 3906872 bytes.

# 这台机器简单说YG内存1G,OG内存2G,总内存4G
# 在这样的配置下,GC运行情况:
/usr/java/jdk1.8.0_162/bin/jstat -gcutil -h5 87303 4s 100
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00   5.51  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547
  0.00   0.00   6.00  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547
  0.00   0.00   6.33  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547
  0.00   0.00   6.94  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547
  0.00   0.00  15.80  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  16.37  22.00  98.42  96.83  32997  157.912 32954 4507.634 4665.547   # 发生了一次YG GC,也就是MinorGC,耗时0.3s(4665.847 - 4665.547)
  0.00   0.00   0.56  22.02  98.42  96.83  32999  157.921 32956 4507.926 4665.847
  0.00   0.00   1.33  22.02  98.42  96.83  32999  157.921 32956 4507.926 4665.847
  0.00   0.00   1.75  22.02  98.42  96.83  32999  157.921 32956 4507.926 4665.847

小结

Java启动参数

参数定义

回收算法

# 查看JVM默认参数中的堆最大值
java -XX:+PrintFlagsFinal -version | grep HeapSize

# 纯jar启动,适用JDK7
java \
 -Xmx512M \
 -XX:MaxPermSize=128M \
 -jar ./biz-1.0.0-SNAPSHOT.jar

# Tomcat启动
java \
 -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties \
 -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager \
 -Djdk.tls.ephemeralDHKeySize=2048 \
 -Dignore.endorsed.dirs= \
 -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar \
 -Dcatalina.base=/usr/local/tomcat \
 -Dcatalina.home=/usr/local/tomcat \
 -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start