idea分析java内存溢出,idea的一次jvm调优

1.准备工作:

这段时间一直在看jvm调优的内容,苦于没有环境让自己练手,周三下午测试人员说测试后台过于慢,之前一直以为是数据库压力太大的问题,后来连上测试环境,发现xmn=150m,新生代只分配了150m,而整个堆最大为2g,然后和运维同事沟通了,建议设置成1G,项目再打包,后台页面基本都秒开,虽然是个测试环境小小的调优,还是比较开心的哈.

测试环境毕竟是测试环境,因为开发没有权限,有东西想改还是要和运维沟通,这几天拿idea试试手,把idea调优下.先说下我的配置,E5 2660 ,8核16线程,16G内存,120G ssd,cpu虽然线程数比较多,但是主频比较低,只有2.2g,idea在我电脑上启动大概需要20s左右.其实前几天就着手做这个事情,但是一直没有办法统计出idea具体的启动时间,周志明大佬在深入jvm一书中是用的eclipse调优,eclipse插件似乎把启动时间写入到全局变量中了,idea我找了半天没找到,今天下午突然发现,其实idea在启动的时候会把启动信息写入到日志中,我的位置是在C:\Users\Administrator.IntelliJIdea2018.1\system\log\idea.log,这里面记录了idea启动都做了哪些事情

ee64269ecb97

image

然后我就以app initialzation took作为时间的标准,后面这个13745ms我不知道它咋算出来的,启动时间到这个点输出一个是11667这个时间才对,然后我就以11667作为idea的启动时间来判断.如果是用linux来截取这个时间的话awk命令就够了,做两次管道

grep 'App initialization took' idea.log | awk '{print $4}'|awk -F ']' '{print $1}'

我是windows上跑的这个,然后就开了个pycharm,用python来处理这个时间,为了能够在idea启动的瞬间,就记录这些信息,我把idea配置在了环境变量里,然后用python启动,就是下面的openIdeaAndTools方法,这里顺便吐槽下pycharm,我配完环境变量,idea死活起不来,然后电脑重启下突然就好了,估计它的terminal缓存了系统的环境变量,不知道算不算bug.

#!/usr/bin/env python

# -*- coding=utf-8 -*-

import fileinput

import os

import threading

import time

class MyThread(threading.Thread):

def __init__(self, arg):

super(MyThread, self).__init__()

self.arg = arg

def run(self):

os.system(self.arg)

def getIdeaStartTime(logPath):

# 获取idea的启动时间

for line in fileinput.input(logPath):

if 'App initialization took' in line:

print(line.split('[')[-1].split(']')[0])

def openIdeaAndTools():

# 这里调用命令是阻塞执行,所以开启线程异步执行

t1 = MyThread('idea64.exe ')

t1.start()

# 防止idea未启动先执行了下面的代码

time.sleep(0.1)

try:

# 调用jps命令

pid = os.popen("jps")

for l in pid:

# 因为idea启动后,jps输出的只有一个vmid,后面没有类名,以此来判断该进程是idea的

id = l.split(' ')[0]

info = l.split(' ')[1]

# 我这台机器上还跑了pycharm,pycharm后面同样没有类名,就把pycharm的vmid去掉

if not info.strip() and int(id) != 1200:

print("idea的Id为" + id)

# 调用jstat

t4 = MyThread('jstat -gcutil ' + id + ' 1000 3')

t4.start()

# 调用jconsole

t2 = MyThread('jconsole ' + id)

# 调用jvisualvm

t3 = MyThread('jvisualvm')

t3.start()

t2.start()

finally:

pid.close()

if __name__ == "__main__":

# getIdeaStartTime(logPath = "C:\\Users\\Administrator\\.IntelliJIdea2018.1\\system\\log\\idea.log")

openIdeaAndTools()

2.未优化前:

首先说下idea的默认配置,在help-edit custom vm options可以看到,配置如下:

-Xms128m //最小堆128

-Xmx750m //最大750

-XX:ReservedCodeCacheSize=240m //代码缓存

-XX:+UseConcMarkSweepGC //使用parnew+cms垃圾回收

-XX:SoftRefLRUPolicyMSPerMB=50 //和soft引用有关

-ea

-Dsun.io.useCanonCaches=false

-Djava.net.preferIPv4Stack=true

-XX:+HeapDumpOnOutOfMemoryError //内存溢出dump出快照

-XX:-OmitStackTraceInFastThrow //抛出堆栈异常

整个的设置还是比较少的,经过多次测试,idea的启动时间大概在18s左右,jstat信息如下(只截取了最后一次测试的前18s)

idea的Id为4440

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT

0.00 100.00 63.01 1.64 94.90 83.95 1 0.011 0 0.000 0.011

0.00 100.00 61.75 16.45 95.42 83.95 3 0.067 0 0.000 0.067

100.00 0.00 28.84 22.12 94.73 83.02 4 0.106 0 0.000 0.106

100.00 0.00 68.97 22.12 94.73 83.02 4 0.106 0 0.000 0.106

0.00 100.00 30.26 31.35 96.83 93.83 5 0.117 1 0.005 0.122

100.00 0.00 0.00 35.89 97.20 94.96 6 0.131 2 0.027 0.158

90.56 0.00 10.54 38.82 97.53 95.00 8 0.141 2 0.027 0.168

100.00 0.00 68.57 46.48 97.53 95.04 10 0.165 2 0.027 0.192

0.00 97.85 22.65 51.15 97.05 92.71 11 0.175 2 0.027 0.202

0.00 100.00 73.74 60.28 97.18 94.92 13 0.225 2 0.027 0.252

0.00 100.00 91.61 60.28 97.18 94.92 13 0.225 2 0.027 0.252

100.00 0.00 4.12 64.82 97.41 93.94 14 0.235 2 0.027 0.262

100.00 0.00 12.22 64.82 97.41 93.94 14 0.235 2 0.027 0.262

100.00 0.00 71.19 64.82 97.41 93.94 14 0.235 2 0.027 0.262

0.00 100.00 43.52 78.72 96.97 92.43 17 0.315 3 0.029 0.343

0.00 100.00 6.02 74.14 95.99 91.92 19 0.341 5 0.053 0.395

100.00 0.00 35.30 67.15 95.24 90.23 20 0.370 6 0.092 0.462

100.00 0.00 35.64 78.42 94.32 87.14 22 0.419 7 0.094 0.512

这18s中可以看出YGC(也就是minor gc)发生了22次,共耗时0.419s,而FGC总共发生了7次,总耗时0.094s,ygc平均每次时间为0.019s,而fgc每次为0.013.

ee64269ecb97

image

从这张图中我们可以看出整个堆的大小先是平稳,后随着使用堆大小的增加逐步变大,而使用堆频繁gc,gc日志如下(前23s,比较长,就直接放服务器了):GC日志:

在0.942s的时候第一次出现了Allocation Failure,分配内存失败,这会导致新生代触发gc,这主要的原因是因为初始堆的大小设置成了128m.好了,有了解决思路,第一步要做的就是先禁掉插件,哈哈,人生就是需要皮一下,idea的log反应出插件的加载似乎是占用了很长一段时间,为了达到优化的效果,先把插件禁掉,在来对gc进行调优.

3.开工:

3.1禁插件

禁掉插件的效果非常明显,测试了三次,时间分别为9296,10034,10363,而最后一次jstat的信息如下:

idea的Id为6512

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT

0.00 100.00 91.18 1.63 94.81 83.95 2 0.028 0 0.000 0.028

0.00 100.00 49.32 16.38 95.35 83.95 3 0.068 0 0.000 0.068

0.00 100.00 50.65 16.38 95.35 83.95 3 0.068 0 0.000 0.068

100.00 0.00 29.37 21.33 94.72 85.34 4 0.089 0 0.000 0.089

100.00 0.00 49.98 21.33 94.72 85.34 4 0.089 0 0.000 0.089

100.00 0.00 90.62 21.33 94.72 85.34 4 0.089 1 0.009 0.099

0.00 100.00 45.10 32.07 97.56 94.05 5 0.112 1 0.009 0.121

100.00 0.00 79.42 36.54 97.22 95.07 6 0.128 2 0.035 0.163

90.65 0.00 92.93 39.97 97.63 95.09 9 0.169 2 0.035 0.204

100.00 0.00 62.49 47.90 97.69 95.15 10 0.195 2 0.035 0.230

10s内发生了10次ygc,2次fgc,其实说实话,这样的性能完全很好了,但是本着这是一篇调优的文章,有点参数还是要改改,看看效果,首先调整的就是整个堆的大小.

3.2调整堆的大小

启动的时候频繁出现了allocation failure,这是因为堆的初始大小过小,导致新生代的内存过小,所以,先设置了以下参数:

-Xms2048m

-Xmx2048m

-Xmn1600m

堆的大小不在动态调整,而是固定在了2g,而新生代则直接分配了1600m,这一次,启动时间为7s,jstat信息如下,前12s:

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT

0.00 0.00 16.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 24.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 32.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 36.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101

0.00 0.00 46.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101

0.00 0.00 54.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101

0.00 0.00 66.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101

0.00 0.00 70.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101

0.00 0.00 78.00 0.00 17.24 19.25 0 0.000 2 0.101 0.101

0.00 0.00 90.00 0.00 17.24 19.25 0 0.000 2 0.258 0.258

0.00 0.00 96.00 0.00 17.24 19.25 0 0.000 2 0.258 0.258

0.00 41.29 20.32 0.00 96.90 94.00 1 0.054 3 0.267 0.321

一直到12s的时候,新生代才出现首次gc,gc日志异常的少,前面一直是cms在做初始标记-并发标记等工作,是在处理老年代内存,前7s只出现了一次老年代的回收,这也说明了大内存对程序的运行帮助那是相当大.

4.246: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(458752K)] 471859K(1933312K), 0.1010486 secs] [Times: user=0.16 sys=0.00, real=0.10 secs]

4.347: [CMS-concurrent-mark-start]

4.347: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

4.347: [CMS-concurrent-preclean-start]

4.349: [CMS-concurrent-preclean: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

4.349: [CMS-concurrent-abortable-preclean-start]

CMS: abort preclean due to time 9.362: [CMS-concurrent-abortable-preclean: 0.256/5.013 secs] [Times: user=13.81 sys=2.18, real=5.01 secs]

9.362: [GC (CMS Final Remark) [YG occupancy: 1022364 K (1474560 K)]9.362: [Rescan (parallel) , 0.1397593 secs]9.502: [weak refs processing, 0.0000532 secs]9.502: [class unloading, 0.0081261 secs]9.510: [scrub symbol table, 0.0068646 secs]9.517: [scrub string table, 0.0007781 secs][1 CMS-remark: 0K(458752K)] 1022364K(1933312K), 0.1575112 secs] [Times: user=1.06 sys=0.00, real=0.16 secs]

9.520: [CMS-concurrent-sweep-start]

9.520: [CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

9.520: [CMS-concurrent-reset-start]

9.523: [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

12.028: [GC (Allocation Failure) 12.028: [ParNew: 1310720K->67643K(1474560K), 0.0544422 secs] 1310720K->67643K(1933312K), 0.0545925 secs] [Times: user=0.51 sys=0.06, real=0.06 secs]

12.083: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(458752K)] 106973K(1933312K), 0.0083509 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

12.091: [CMS-concurrent-mark-start]

12.091: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

12.091: [CMS-concurrent-preclean-start]

12.094: [CMS-concurrent-preclean: 0.003/0.003 secs] [Times: user=0.16 sys=0.01, real=0.00 secs]

12.095: [CMS-concurrent-abortable-preclean-start]

16.748: [CMS-concurrent-abortable-preclean: 4.303/4.653 secs] [Times: user=32.20 sys=1.31, real=4.65 secs]

16.748: [GC (CMS Final Remark) [YG occupancy: 737368 K (1474560 K)]16.748: [Rescan (parallel) , 0.0331714 secs]16.782: [weak refs processing, 0.0000574 secs]16.782: [class unloading, 0.0177730 secs]16.800: [scrub symbol table, 0.0229479 secs]16.823: [scrub string table, 0.0011784 secs][1 CMS-remark: 0K(458752K)] 737368K(1933312K), 0.0792557 secs] [Times: user=0.48 sys=0.00, real=0.08 secs]

16.828: [CMS-concurrent-sweep-start]

16.828: [CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

16.828: [CMS-concurrent-reset-start]

16.829: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

21.380: [GC (Allocation Failure) 21.380: [ParNew: 1378363K->124879K(1474560K), 0.0719474 secs] 1378363K->124879K(1933312K), 0.0721093 secs] [Times: user=0.53 sys=0.08, real=0.07 secs]

23.844: [GC (Allocation Failure) 23.845: [ParNew: 1435599K->78246K(1474560K), 0.2064758 secs] 1435599K->139746K(1933312K), 0.2066017 secs] [Times: user=1.06 sys=0.03, real=0.21 secs]

3.2 新生代调优

为了能够说明某些问题,我把参数又调回了初始参数,因为如果用上面的参数继续跑,基本没法调了,用原始参数对新生代进行调优,在此首先需要介绍下parnew收集器.

3.2.1 parnew介绍

parnew是作为新生代的垃圾回收器,当然也就采用了复制的算法,首先进行可达性分析,stop the world,然后再进行垃圾回收,将eden和s0的数据复制到s1当中去,它和serial的区别就在于parnew是多线的,然后关于parnew的文档,很可惜的是我在oracle官网上没有找到,全是介绍g1,cms和parallel scavenge的,但是官网给出的常见问题列表中有个问题比较有意思,地址, What is the Parallel Young Generation collector (-XX:+UseParNewGC)?官网的回复是parNew和parallel表现出来的东西很像,但是在内部实现上不一样,也就是说关于运行的机制大都差不多,毕竟都是并行的,但是在代码实现上差很多,还有一个不同点就是parnew是能和cms搭配使用的,而parallel不行.

3.2.2调优参数

然后我就用了初始的vm参数中又加了几个参数,(jvm的参数值介绍,官网地址)

第一个参数是-Xmn400m,分配给新生代400m的内存,另外固定住堆的大小为750M,防止因堆的大小不够用而频繁gc.

第二个是-XX:ParallelGCThreads,这个参数用于调整线程数,在我机器上默认是13个

ee64269ecb97

image

第三个是-XX:+ScavengeBeforeFullGC,minor gc优先于fgc,这样的好处就是往往会存在着新生代的对象持有老年代的引用(所以老年代的gc,通常会伴随着fgc),这样在fgc之前先清理掉一部分年轻代对象,会减少可达性分析的成本(在本次实验中,似乎没有展示出优势,fgc次数比较少).

-Xms750m

-Xmx750m

-Xmn400m

-XX:ParallelGCThreads=20

-XX:+ScavengeBeforeFullGC

3.2.3调优结果

这一次启动时间9s,然后看下前9s的jstat信息:

S0 S1 E O M CCS YGC YGCT FGC FGCT GCT

0.00 0.00 28.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 50.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 52.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 60.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 64.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 68.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 76.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000

0.00 0.00 84.00 0.00 17.24 19.25 0 0.000 1 0.089 0.089

0.00 66.86 5.20 0.00 96.69 92.35 1 0.025 1 0.089 0.115

0.00 66.86 33.40 0.00 96.69 92.35 1 0.025 1 0.089 0.115

由上面结果可得知,9s中共发生1次ygc,这我觉得益于-Xms750m,-Xmx750m,-Xmn400m,将新生代的内存大小固定在了400m,而减少了频繁的gc.

3.2.4parnew gc日志解读

然后这里顺便解读下gc的日志,关于parnew的:

9.399: [GC (Allocation Failure) 9.399: [ParNew: 327680K->27386K(368640K), 0.0253901 secs] 327680K->27386K(727040K), 0.0255930 secs] [Times: user=0.06 sys=0.06, real=0.03 secs]

9.399代表项目启动时间,Allocation Failure代表gc的原因,因为内存分配失败,ParNew代表使用的ParNew算法,327680K->27386K(368640K)代表,新生代回收前->新生代回收后(整个新生代大小),327680K->27386K(727040K)代表,整个堆回收前->整个堆回收后(整个堆的大小),0.0255930s代表此次垃圾回收的时间,[Times: user=0.06 sys=0.06, real=0.03 secs] 代表用户线程执行的时间,系统线程执行的时间,总共耗时,因为用户线程和系统线程并行执行,所以总耗时往往比user+sys时间要小.

3.3 老年代调优

3.3.1 cms介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您在重构项目时遇到了内存溢出问题,可以尝试以下解决方案: 1. 增加IDEA的最大内存限制。您说已经尝试过这个方法,但是没有效果,可能是因为您修改的配置文件不正确。您可以通过以下步骤来修改IDEA的最大内存限制: - 打开IDEA安装目录下的bin文件夹,找到idea.exe.vmoptions(Windows)或idea.vmoptions(Linux/Mac)文件。 - 用文本编辑器打开这个文件,找到-Xmx参数,将其值增加到您认为足够的大小。例如,将-Xmx512m改为-Xmx1024m。 - 保存文件并重新启动IDEA。 2. 增加JVM的最大内存限制。如果您已经将IDEA的最大内存限制设置为足够大的值,但仍然遇到内存溢出问题,可能是因为JVM的最大内存限制太小了。您可以通过以下步骤来修改JVM的最大内存限制: - 打开IDEA安装目录下的bin文件夹,找到idea.exe(Windows)或idea(Linux/Mac)文件。 - 在文件开头添加以下行: ``` set JVM_OPTIONS="-Xmx1024m" ``` 将-Xmx的值设置为您认为足够的大小。 - 保存文件并重新启动IDEA。 3. 化重构过程。如果您的项目非常大,重构可能需要很长时间,并且需要大量的内存。您可以尝试以下化方法: - 将重构拆分为多个步骤,每个步骤只处理一部分代码。 - 尝试使用更高效的重构方法,例如“Extract Method”而不是“Inline Method”。 - 尝试将项目拆分为多个模块,并且只对需要重构的模块进行操作。 希望这些方法能够帮助您解决内存溢出问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值