竹笋

注册

 

发新话题 回复该主题

超赞不愧是美团内部的JVM学习手册,从 [复制链接]

1#

Hello,今天给各位童鞋们分享的是JVM,赶紧拿出小本子记下来吧

一、内存溢出

内存溢出的原因:程序在申请内存时,没有足够的空间。

1.栈溢出

方法死循环递归调用(StackOverflowError)、不断建立线程(OutOfMemoryError)。

2.堆溢出

不断创建对象,分配对象大于最大堆的大小(OutOfMemoryError)。

3.直接内存

JVM分配的本地直接内存大小大于JVM的限制,可以通过-XX:MaxDirectMemorySize来设置(不设置的话默认与堆内存最大值一样,也会出现OOM异常)。

4.方法区溢出

一个类要被垃圾收集器回收掉,判定条件是比较苛刻的,在经常动态生产大量Class的应用中,CGLIb字节码增强,动态语言,大量JSP(JSP第一次运行需要编译成Java类),基于OSGi的应用(同一个类,被不同的加载器加载也会设为不同的类),都可能会导致OOM。

二、内存泄露

程序在申请内存后,无法释放已申请的内存空间,导致这一部分的原因主要是代码写的不合理,比如以下几种情况。

1.长生命周期的对象持有短生命周期对象的引用

例如将ArrayList设置为静态变量,然后不断地向ArrayList中添加对象,则ArrayList容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。

2.连接未关闭

如数据库连接、网络连接和IO连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。

3.变量作用域不合理

例如/p>

一个变量的定义的作用范围大于其使用范围。

如果没有及时地把对象设置为null。

4.内部类持有外部类

Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄露(可以理解为:垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)。

解决办法:将非静态内部类改为静态内部类,即加上static修饰,例如:

5.Hash值改变

在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露。

使用例子来说明。

三、内存溢出和内存泄漏辨析

内存溢出:实实在在的内存空间不足导致。内存泄漏:该释放的对象没有释放,常见于使用容器保存元素的情况下。如何避免:

内存溢出:检查代码以及设置足够的空间。内存泄漏:一定是代码有问题,往往很多情况下,内存溢出往往是内存泄漏造成的。四、了解MAT

mat是一个内存泄露的分析工具。

1.浅堆和深堆

浅堆(ShallowHeap):是指一个对象所消耗的内存。深堆(RetainedHeap):这个对象被GC回收后,可以真实释放的内存大小,也就是只能通过对象被直接或间接访问到的所有对象的集合。通俗地说,就是一个对象包含(引用)的所有对象的大小,如图:

2.MAT的使用

内存溢出例子演示

参数说明:

-Xms5m堆初始大小5M

-Xmx5m堆最大大小5M

-XXPrintGCDetails打印gc日志详情

-XXHeapDumpOnOutOfMemoryError输出内存溢出文件

-XX:HeapDumpPath=D:/oomDump/dump.hprof内存溢出文件保存位置,此文件用于MAT分析

设置参数运行后,内存溢出,程序结束,然后我们就可以用下载好的MAT来分析了,当然MAT也只是分析猜想,并不代表一定是这个原因导致内存溢出。

打开我们保存的文件目录进行分析。

分析结果。

此时可以查看详情查看具体原因,当然这个原因也只是一种猜想。

五、JDK提供的一些工具

所有的工具都在jdk的安装bin目录下,比如我的在C:\MyProgramFiles\Java\jdk1.8.0_\bin。

其中一般情况命令行在线上服务器上使用,可视化工具在本地使用,当然如果你的线上服务器允许远程的话也可以使用可视化工具。

六、GC调优

1.GC调优重要参数

生产环境推荐开启

-XXHeapDumpOnOutOfMemoryError

输出内存溢出文件-XX:HeapDumpPath=D:/oomDump/dump.hprof

内存溢出文件保存位置,此文件用于MAT分析当然,一般Linux服务器可以设置为./java_pidpid.hprof默认为Java进程启动位置调优之前开始,调优之后关闭

-XXPrintGC

调试跟踪之打印简单的GC信息参数:-XXPrintGCDetails和-XX:+PrintGCTimeStamps

打印详细的GC信息-Xlogger:logpath:log/gc.log

设置gc的日志路,将gc.log的路径设置到当前目录的log目录下.应用场景:将gc的日志独立写入日志文件,将GC日志与系统业务日志进行了分离,方便开发人员进行追踪分析考虑使用

-XX:+PrintHeapAtGC

打印推信息,获取Heap在每次垃圾回收前后的使用状况-XX:+TraceClassLoading

在系统控制台信息中看到class加载的过程和具体的class信息,可用以分析类的加载顺序以及是否可进行精简操作-XX:+DisableExplicitGC

禁止在运行期显式地调用System.gc()2.GC调优的原则(很重要)

大多数的java应用不需要GC调优大部分需要GC调优的的,不是参数问题,是代码问题在实际使用中,分析GC情况优化代码比优化GC参数要多得多GC调优是最后的手段调优的目的

GC的时间够小GC的次数够少发生FullGC的周期足够的长,时间合理,最好是不发生注:如果满足下面的指标,则一般不需要进行GC调优

MinorGC执行时间不到50msMinorGC执行不频繁,约10秒一次FullGC执行时间不到1sFullGC执行频率不算频繁,不低于10分钟1次3.GC调优步骤

1、监控GC的状态使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化。

2、分析结果,判断是否需要优化如果各项参数设置合理。

系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化。

如果GC时间超过1秒,或者频繁GC,则必须优化。

3、调整GC类型和内存分配如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行测试,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择。

4、不断的分析和调整通过不断的试验和试错,分析并找到最合适的参数5,全面应用参数如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。

分析GC日志

主要

分享 转发
TOP
发新话题 回复该主题