JVM优化(一)JVM简介及内存溢出解决方案

 
了解下我们为什么要学习 JVM 优化
掌握 jvm 的运行参数以及参数的设置
掌握 jvm 的内存模型(堆内存)
掌握 jamp 命令的使用以及通过 MAT 工具进行分析
掌握定位分析内存溢出的方法
掌握 jstack 命令的使用
掌握 VisualJVM 工具的使用
 
1 、我们为什么要对 jvm 做优化?
在本地开发环境中我们很少会遇到需要对 jvm 进行优化的需求,但是到了生产环境,我们
可能将有下面的需求:
运行的应用 卡住了 ,日志不输出,程序没有反应
服务器的 CPU 负载突然升高
在多线程应用下,如何分配线程的数量?
……
我们将对 jvm 有更深入的学习,我们不仅要让程序能跑起来,而且是可以
跑的更快!可以分析解决在生产环境中所遇到的各种 棘手 的问题。
说明:本套课程使用的 jdk 版本为 1.8
2 jvm 的运行参数
jvm 中有很多的参数可以进行设置,这样可以让 jvm 在各种环境中都能够高效的运行。
绝大部分的参数保持默认即可。
2.1 、三种参数类型
jvm 的参数类型分为三类,分别是:
 
标准参数
   -help
  -version
-X 参数 (非标准参数)
   -Xint
  -Xcomp
-XX 参数(使用率较高)
   -XX:newSize
   -XX:+UseSerialGC
2.2 、标准参数
jvm 的标准参数,一般都是很稳定的,在未来的 JVM 版本中不会改变,可以使用 java -help
检索出所有的标准参数。
 
[root@node01 ~] # java ‐help
用法 : java [‐options] class [args...]
( 执行类 )
java [‐options] ‐jar jarfile [args...]
( 执行 jar 文件 )
其中选项包括 :
‐d32 使用 32 位数据模型 ( 如果可用 )
‐d64 使用 64 位数据模型 ( 如果可用 )
‐server 选择 "server" VM
默认 VM server,
因为您是在服务器类计算机上运行。
‐cp < 目录和 zip/jar 文件的类搜索路径 >
‐classpath < 目录和 zip/jar 文件的类搜索路径 >
: 分隔的目录 , JAR 档案
ZIP 档案列表 , 用于搜索类文件。
‐D < 名称 > = < >
设置系统属性
‐verbose :[class|gc|jni]
启用详细输出
‐version 输出产品版本并退出
‐version :< >
警告 : 此功能已过时 , 将在
未来发行版中删除。
需要指定的版本才能运行
‐showversion 输出产品版本并继续
‐jre‐restrict‐search | ‐no‐jre‐restrict‐search
警告 : 此功能已过时 , 将在
未来发行版中删除。
在版本搜索中包括 / 排除用户专用 JRE
? ‐help 输出此帮助消息
‐X 输出非标准选项的帮助
‐ea [:<packagename>...|:<classname>]
‐enableassertions [:<packagename>...|:<classname>]
按指定的粒度启用断言
‐da [:<packagename>...|:<classname>]
‐disableassertions [:<packagename>...|:<classname>]
禁用具有指定粒度的断言
‐esa | ‐enablesystemassertions
启用系统断言
‐dsa | ‐disablesystemassertions
禁用系统断言
‐agentlib :<libname>[ = < 选项 >]
加载本机代理库 <libname>, 例如 ‐agentlib :hprof
另请参阅 ‐agentlib :jdwp = help ‐agentlib :hprof = help
‐agentpath :<pathname>[ = < 选项 >]
按完整路径名加载本机代理库
‐javaagent :<jarpath>[ = < 选项 >]
加载 Java 编程语言代理 , 请参阅 java.lang.instrument
‐splash :<imagepath>
使用指定的图像显示启动屏幕
[root@node01 ~] # java ‐version
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
# ‐showversion 参数是表示,先打印版本信息,再执行后面的命令,在调试时非常有用,
后面会使用到。
public class TestJVM {
  public static void main ( String [] args ) {
    String str = System . getProperty ( "str" );
   if ( str == null ) {
     System . out . println ( "aa" );
   } else {
    System . out . println ( str );
   }
 }
}
 
进行编译、测试:
测试:
# 编译
[root@node01 test] # javac TestJVM.java
# 测试
[root@node01 test] # java TestJVM
aa
[root@node01 test] # java ‐Dstr=123 TestJVM
123
2.2.2 -server -client 参数
可以通过 -server -client 设置 jvm 的运行参数。
它们的区别是 Server VM 的初始堆空间会大一些,默认使用的是并行垃圾回收器,启
动慢运行快。
Client VM 相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它
的目标是为了让 JVM 的启动速度更快,但运行速度会比 Serverm 模式慢些。
JVM 在启动的时候会根据硬件和操作系统自动选择使用 Server 还是 Client 类型的
JVM
32 位操作系统
如果是 Windows 系统,不论硬件配置如何,都默认使用 Client 类型的 JVM
如果是其他操作系统上,机器配置有 2GB 以上的内存同时有 2 个以上 CPU 的话默
认使用 server 模式,否则使用 client 模式。
64 位操作系统
只有 server 类型,不支持 client 类型。
 
 
[root@node01 test] # java ‐client ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
aa
[root@node01 test] # java ‐server ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
aa
 
2.3 -X 参数
jvm -X 参数是非标准参数,在不同版本的 jvm 中,参数可能会有所不同,可以通过 java -
X 查看非标准参数。
 
[root@node01 test] # java ‐X
‐Xmixed 混合模式执行 ( 默认 )
‐Xint 仅解释模式执行
‐Xbootclasspath :< : 分隔的目录和 zip/jar 文件 >
设置搜索路径以引导类和资源
‐Xbootclasspath /a:< : 分隔的目录和 zip/jar 文件 >
附加在引导类路径末尾
‐Xbootclasspath /p:< : 分隔的目录和 zip/jar 文件 >
置于引导类路径之前
‐Xdiag 显示附加诊断消息
‐Xnoclassgc 禁用类垃圾收集
‐Xincgc 启用增量垃圾收集
‐Xloggc :<file> GC 状态记录在文件中 ( 带时间戳 )
‐Xbatch 禁用后台编译
‐Xms <size> 设置初始 Java 堆大小
‐Xmx <size> 设置最大 Java 堆大小
‐Xss <size> 设置 Java 线程堆栈大小
‐Xprof 输出 cpu 配置文件数据
‐Xfuture 启用最严格的检查 , 预期将来的默认值
‐Xrs 减少 Java/VM 对操作系统信号的使用 ( 请参阅文档 )
‐Xcheck :jni JNI 函数执行其他检查
‐Xshare :off 不尝试使用共享类数据
‐Xshare :auto 在可能的情况下使用共享类数据 ( 默认 )
‐Xshare :on 要求使用共享类数据 , 否则将失败。
‐XshowSettings 显示所有设置并继续
‐XshowSettings :all
显示所有设置并继续
‐XshowSettings :vm 显示所有与 vm 相关的设置并继续
‐XshowSettings :properties
显示所有属性设置并继续
‐XshowSettings :locale
显示所有与区域设置相关的设置并继续
‐X 选项是非标准选项 , 如有更改 , 恕不另行通知。
2.3.1 -Xint -Xcomp -Xmixed
在解释模式 (interpreted mode) 下, -Xint 标记会强制 JVM 执行所有的字节码,当然这
会降低运行速度,通常低 10 倍或更多。
 
-Xcomp 参数与它( -Xint )正好相反, JVM 在第一次使用时会把所有的字节码编译成
本地代码,从而带来最大程度的优化。
然而,很多应用在使用 -Xcomp 也会有一些性能损失,当然这比使用 -Xint 损失的
少,原因是 -xcomp 没有让 JVM 启用 JIT 编译器的全部功能。 JIT 编译器可以对是否
需要编译做判断,如果所有代码都进行编译的话,对于一些只执行一次的代码就
没有意义了。
-Xmixed 是混合模式,将解释模式与编译模式进行混合使用,由 jvm 自己决定,这是
jvm 默认的模式,也是推荐使用的模式。
示例:强制设置运行模式
# 强制设置为解释模式
[root@node01 test] # java ‐showversion ‐Xint TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, interpreted mode)
aa
 
# 强制设置为编译模式
[root@node01 test] # java ‐showversion ‐Xcomp TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, compiled mode)
aa
# 注意:编译模式下,第一次执行会比解释模式下执行慢一些,注意观察。
# 默认的混合模式
[root@node01 test] # java ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
aa
 
2.4 -XX 参数
-XX 参数也是非标准参数,主要用于 jvm 的调优和 debug 操作。
-XX 参数的使用有 2 种方式,一种是 boolean 类型,一种是非 boolean 类型:
boolean 类型
格式: -XX:[+-]
如: -XX:+DisableExplicitGC 表示禁用手动调用 gc 操作,也就是说调用
System.gc() 无效
boolean 类型
格式: -XX:
如: -XX:NewRatio=1 表示新生代和老年代的比值
用法:
[root@node01 test] # java ‐showversion ‐XX:+DisableExplicitGC TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
aa
2.5 -Xms -Xmx 参数
-Xms -Xmx 分别是设置 jvm 的堆内存的初始大小和最大大小。
-Xmx2048m :等价于 -XX:MaxHeapSize ,设置 JVM 最大堆内存为 2048M
-Xms512m :等价于 -XX:InitialHeapSize ,设置 JVM 初始堆内存为 512M
适当的调整 jvm 的内存大小,可以充分利用服务器资源,让程序跑的更快。
示例:
[root@node01 test] # java ‐Xms512m ‐Xmx2048m TestJVM
aa
2.6 、查看 jvm 的运行参数
有些时候我们需要查看 jvm 的运行参数,这个需求可能会存在 2 种情况:
第一,运行 java 命令时打印出运行参数;
第二,查看正在运行的 java 进程的参数;
 
 
2.6.1 、运行 java 命令时打印参数
运行 java 命令时打印参数,需要添加 -XX:+PrintFlagsFinal 参数即可。
 
[root@node01 test] # java ‐XX:+PrintFlagsFinal ‐version
[Global flags]
uintx AdaptiveSizeDecrementScaleFactor = 4
{product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10
{product}
uintx AdaptiveSizePausePolicy = 0
{product}
uintx AdaptiveSizePolicyCollectionCostMargin = 50
{product}
uintx AdaptiveSizePolicyInitializingSteps = 20
{product}
uintx AdaptiveSizePolicyOutputInterval = 0
{product}
uintx AdaptiveSizePolicyWeight = 10
{product}
uintx AdaptiveSizeThroughPutPolicy = 0
{product}
uintx AdaptiveTimeWeight = 25
{product}
bool AdjustConcurrency = false
{product}
bool AggressiveOpts = false
{product}
intx AliasLevel = 3
{C2 product}
bool AlignVector = true
{C2 product}
intx AllocateInstancePrefetchLines = 1
{product}
intx AllocatePrefetchDistance = 256
{product}
intx AllocatePrefetchInstr = 0
{product}
………………………… …………………………………………
bool UseXmmI2D = false
{ARCH product}
bool UseXmmI2F = false
{ARCH product}
bool UseXmmLoadAndClearUpper = true
{ARCH product}
bool UseXmmRegToRegMoveAll = true
{ARCH product}
bool VMThreadHintNoPreempt = false
{product}
intx VMThreadPriority = ‐1
{product}
intx VMThreadStackSize = 1024
{pd product}
intx ValueMapInitialSize = 11
{C1 product}
intx ValueMapMaxLoopSize = 8
{C1 product}
intx ValueSearchLimit = 1000
{C2 product}
bool VerifyMergedCPBytecodes = true
{product}
bool VerifySharedSpaces = false
{product}
intx WorkAroundNPTLTimedWaitHang = 1
{product}
uintx YoungGenerationSizeIncrement = 20
{product}
uintx YoungGenerationSizeSupplement = 80
{product}
uintx YoungGenerationSizeSupplementDecay = 8
{product}
uintx YoungPLABSize = 4096
{product}
bool ZeroTLAB = false
{product}
intx hashCode = 5
{product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
由上述的信息可以看出,参数有 boolean 类型和数字类型,值的操作符是 = := ,分别代
表默认值和被修改的值。
示例:
java ‐XX : + PrintFlagsFinal ‐XX : + VerifySharedSpaces ‐version
intx ValueMapInitialSize = 11
{C1 product}
intx ValueMapMaxLoopSize = 8
{C1 product}
intx ValueSearchLimit = 1000
{C2 product}
bool VerifyMergedCPBytecodes = true
{product}
bool VerifySharedSpaces : = true
{product}
intx WorkAroundNPTLTimedWaitHang = 1
{product}
uintx YoungGenerationSizeIncrement = 20
{product}
uintx YoungGenerationSizeSupplement = 80
{product}
uintx YoungGenerationSizeSupplementDecay = 8
{product}
uintx YoungPLABSize = 4096
{product}
bool ZeroTLAB = false
{product}
intx hashCode = 5
{product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1 .8.0_141‐b15)
Java HotSpot(TM) 64 ‐Bit Server VM (build 25 .141‐b15, mixed mode)
# 可以看到 VerifySharedSpaces 这个参数已经被修改了。
 
2.6.2 、查看正在运行的 jvm 参数
如果想要查看正在运行的 jvm 就需要借助于 jinfo 命令查看。
 
首先,启动一个 tomcat 用于测试,来观察下运行的 jvm 参数。
cd /tmp/
rz 上传
tar ‐xvf apache‐tomcat‐7.0.57.tar.gz
cd apache‐tomcat‐7.0.57
cd bin/
./startup.sh
#http://192.168.40.133:8080/ 进行访问
 
# 查看所有的参数,用法: jinfo ‐flags < 进程 id>
# 通过 jps 或者 jps ‐l 查看 java 进程
[root@node01 bin] # jps
6346 Jps
6219 Bootstrap
[root@node01 bin] # jps ‐l
6358 sun.tools.jps.Jps
6219 org.apache.catalina.startup.Bootstrap
 
[root@node01 bin] #
[root@node01 bin] # jinfo ‐flags 6219
Attaching to process ID 6219 , please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25 .141‐b15
Non‐default VM flags:
 
 
 
3 jvm 的内存模型

1  jdk1.7堆内存模型

1.1  Young 年轻区(代)

 

Young 区被划分为三部分, Eden 区和两个大小严格相同的 Survivor 区,其中, Survivor 区间中,某一时刻只有

其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在 Eden 区间变满的时候, GC 就会将存活的对

象移到空闲的 Survivor 区间中,根据 JVM 的策略,在经过几次垃圾收集后,任然存活于 Survivor 的对象将被移

动到 Tenured 区间。

 

1.2 Tenured 年老区

 

Tenured 区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在 Young 复制转移一定的次数以

后,对象就会被转移到 Tenured 区,一般如果系统中用了 application 级别的缓存,缓存中的对象往往会被转

移到这一区间。

 

1.3 Perm 永久区

 

Perm 代主要保存 class,method,fifiled 对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在

涉及到热部署的应用服务器的时候,有时候会遇到 java.lang.OutOfMemoryError : PermGen space 的错误,

造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的 class 没有被卸载掉,这样就造

成了大量的 class 对象保存在了 perm 中,这种情况下,一般重新启动应用服务器可以解决问题。

 

2 jdk1.8堆内存模型

2.1 jdk1.8 年轻代和老年代几乎一样,jdk 1.8中只有年轻代和年老代组成 ,注意的是1.8中变化最大的perm区用matespace进行了替换,jdk1.8中matespace占用的是本地内存空间,而不是虚拟机的内存空间

 

2.2 为什么要废弃永久代

《1》 移除永久代是为融合 HotSpot JVM 与 JRockit VM 而做出的努力,因为 JRockit 没有永久代,不需要配置永久代。

《2》 由于永久代内存经常不够用或发生内存泄露,爆出异常 java.lang.OutOfMemoryError: PermGen 。 基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间。

 
3.4 、通过 jstat 命令进行查看堆内存使用情况
jstat 命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [- 命令选项 ] [vmid] [ 间隔时间 / 毫秒 ] [ 查询次数 ]
3.4.1 、查看 class 加载统计
 
[root@node01 ~] # jps
7080 Jps
6219 Bootstrap
[root@node01 ~] # jstat ‐class 6219
Loaded Bytes Unloaded Bytes Time
3273 7122 .3 0 0 .0
 
说明:
Loaded :加载 class 的数量
Bytes :所占用空间大小
Unloaded :未加载数量
Bytes :未加载占用空间
Time :时间
 
 
3.4.2 、查看编译统计
[root@node01 ~] # jps
7080 Jps
6219 Bootstrap
[root@node01 ~] # jstat ‐class 6219
Loaded Bytes Unloaded Bytes Time
3273 7122 .3 0 0 .0 3 .98
[root@node01 ~] # jstat ‐compiler 6219
Compiled Failed Invalid Time FailedType FailedMethod
2376 1 0 8 .04 1
 
 
说明:
Compiled :编译数量。
Failed :失败数量
Invalid :不可用数量
Time :时间
FailedType :失败类型
FailedMethod :失败的方法
 
 
 
3.4.3 、垃圾回收统计
[root@node01 ~] # jstat ‐gc 6219
S0C S1C S0U S1U EC EU OC OU MC
MU CCSC CCSU YGC YGCT FGC FGCT GCT
9216 .0 8704 .0 0 .0 6127 .3 62976 .0 3560 .4 33792 .0 20434 .9
23808 .0 23196 .1 2560 .0 2361 .6 7 1 .078 1 0 .244 1 .323
# 也可以指定打印的间隔和次数,每 1 秒中打印一次,共打印 5
[root@node01 ~] # jstat ‐gc 6219 1000 5
S0C S1C S0U S1U EC EU OC OU MC
MU CCSC CCSU YGC YGCT FGC FGCT GCT
9216 .0 8704 .0 0 .0 6127 .3 62976 .0 3917 .3 33792 .0 20434 .9
23808 .0 23196 .1 2560 .0 2361 .6 7 1 .078 1 0 .244 1 .323
9216 .0 8704 .0 0 .0 6127 .3 62976 .0 3917 .3 33792 .0 20434 .9
23808 .0 23196 .1 2560 .0 2361 .6 7 1 .078 1 0 .244 1 .323
9216 .0 8704 .0 0 .0 6127 .3 62976 .0 3917 .3 33792 .0 20434 .9
23808 .0 2319
 
 
 
说明:
S0C :第一个 Survivor 区的大小( KB
S1C :第二个 Survivor 区的大小( KB
S0U :第一个 Survivor 区的使用大小( KB
S1U :第二个 Survivor 区的使用大小( KB
EC Eden 区的大小( KB
EU Eden 区的使用大小( KB
OC Old 区大小( KB
OU Old 使用大小( KB
MC :方法区大小( KB
MU :方法区使用大小( KB
CCSC :压缩类空间大小( KB
CCSU :压缩类空间使用大小( KB
YGC :年轻代垃圾回收次数
YGCT :年轻代垃圾回收消耗时间
 
FGC :老年代垃圾回收次数
FGCT :老年代垃圾回收消耗时间
GCT :垃圾回收消耗总时间
4 jmap 的使用以及内存溢出分析
前面通过 jstat 可以对 jvm 堆的内存进行统计分析,而 jmap 可以获取到更加详细的内容,
如:内存使用情况的汇总、对内存溢出的定位与分析。
4.1 、查看内存使用情况
 
[root@node01 ~] # jmap ‐heap 6219
Attaching to process ID 6219 , please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25 .141‐b15
using thread‐local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration: # 堆内存配置信息
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 488636416 (466.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 162529280 (155.0MB)
OldSize = 20971520 (20.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize
Heap Usage: # 堆内存的使用情况
PS Young Generation # 年轻代
Eden Space:
capacity = 123731968 (118.0MB)
used = 1384736 (1.320587158203125MB)
free = 122347232 (116.67941284179688MB)
1 .1191416594941737% used
From Space:
capacity = 9437184 (9.0MB)
used = 0 (0.0MB)
free = 9437184 (9.0MB)
0 .0% used
To Space:
capacity = 9437184 (9.0MB)
used = 0 (0.0MB)
free = 9437184 (9.0MB)
0 .0% used
 
PS Old Generation # 年老代
capacity = 28311552 (27.0MB)
used = 13698672 (13.064071655273438MB)
free = 14612880 (13.935928344726562MB)
48 .38545057508681% used
13648 interned Strings occupying 1866368 bytes.
 
4.2 、查看内存中对象数量及大小
 
# 查看所有对象,包括活跃以及非活跃的
jmap ‐histo <pid> | more
# 查看活跃对象
jmap ‐histo :live <pid> | more
[root@node01 ~] # jmap ‐histo:live 6219 | more
num #instances #bytes class name
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
1 : 37437 7914608 [C
2 : 34916 837984 java.lang.String
3 : 884 654848 [B
4 : 17188 550016 java.util.HashMap $Node
5 : 3674 424968 java.lang.Class
6 : 6322 395512 [Ljava.lang.Object;
7 : 3738 328944 java.lang.reflect.Method
8 : 1028 208048 [Ljava.util.HashMap $Node ;
9 : 2247 144264 [I
10 : 4305 137760
java.util.concurrent.ConcurrentHashMap $Node
11 : 1270 109080 [Ljava.lang.String;
12 : 64 84128
[Ljava.util.concurrent.ConcurrentHashMap $Node ;
13 : 1714 82272 java.util.HashMap
14 : 3285 70072 [Ljava.lang.Class;
15 : 2888 69312 java.util.ArrayList
16 : 3983 63728 java.lang.Object
17 : 1271 61008
org.apache.tomcat.util.digester.CallMethodRule
18 : 1518 60720 java.util.LinkedHashMap $Entry
19 : 1671 53472
com.sun.org.apache.xerces.internal.xni.QName
20 : 88 50880 [Ljava.util.WeakHashMap $Entry ;
21 : 618 49440 java.lang.reflect.Constructor
22 : 1545 49440 java.util.Hashtable $Entry
23 : 1027 41080 java.util.TreeMap $Entry
24 : 846 40608
org.apache.tomcat.util.modeler.AttributeInfo
25 : 142 38032 [S
 
 
26 : 946 37840 java.lang.ref.SoftReference
27 : 226 36816 [[C
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
# 对象说明
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如 [I 表示 int[]
[L + 类名 其他对象
 
 
4.3 、将内存使用情况 dump 到文件中
有些时候我们需要将 jvm 当前内存中的情况 dump 到文件中,然后对它进行分析, jmap
是支持 dump 到文件中
# 用法:
jmap ‐dump :format = b ,file = dumpFileName <pid>
 

 

可以看到已经在 /tmp 下生成了 dump.dat 的文件。
4.4 、通过 jhat dump 文件进行分析
在上一小节中,我们将 jvm 的内存 dump 到文件中,这个文件是一个二进制的文件,不方
便查看,这时我们可以借助于 jhat 工具进行查看。
 
# 用法:
jhat ‐port <port> <file>
# 示例:
[root@node01 tmp] # jhat ‐port 9999 /tmp/dump.dat
Reading from /tmp/dump.dat...
Dump file created Mon Sep 10 01 :04:21 CST 2018
Snapshot read, resolving...
Resolving 204094 objects...
Chasing references, expect 40
dots........................................
Eliminating duplicate references........................................
Snapshot resolved.
Started HTTP server on port 9999
Server is ready
 
5 、实战:内存溢出的定位与分析
内存溢出在实际的生产环境中经常会遇到,比如,不断的将数据写入到一个集合中,出
现了死循环,读取超大的文件等等,都可能会造成内存溢出。
如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正
常还是非正常情况,如果是正常的需求,就应该考虑加大内存的设置,如果是非正常需
求,那么就要对代码进行修改,修复这个 bug
首先,我们得先学会如何定位问题,然后再进行分析。如何定位问题呢,我们需要借助
jmap MAT 工具进行定位分析。
接下来,我们模拟内存溢出的场景。
5.1 、模拟内存溢出
编写代码,向 List 集合中添加 100 万个字符串,每个字符串由 1000 UUID 组成。如果程
序能够正常执行,最后打印 ok
 
 
 
public class TestJvmOutOfMemory {
public static void main ( String [] args ) {
List< Object > list = new ArrayList<> ();
for ( int i = 0 ; i < 10000000 ; i++ ) {
String str = "" ;
for ( int j = 0 ; j < 1000 ; j++ ) {
str += UUID . randomUUID (). toString ();
}
list . add ( str );
}
System . out . println ( "ok" );
}
}
 
 
 
 
 
6 jstack 的使用
有些时候我们需要查看下 jvm 中的线程执行情况,比如,发现服务器的 CPU 的负载突然增
高了、出现了死锁、死循环等,我们该如何分析呢?
由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么问题,所以就需要
看下 jvm 的内部线程的执行情况,然后再进行分析查找出原因。
这个时候,就需要借助于 jstack 命令了, jstack 的作用是将正在运行的 jvm 的线程情况进
行快照,并且打印出来:
# 用法: jstack <pid>
[root@node01 bin] # jstack 2203
Full thread dump Java HotSpot(TM) 64 ‐Bit Server VM (25.141‐b15 mixed
mode):
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值