Java内存溢出的原因有哪些?Java进程占用内存构成有哪些?

上一篇 / 下一篇  2017-07-25 16:56:56

很多人的理解是,Java进程占用的内存就是堆内存占用,再进一步就是Perm/元数据区的占用。

Java面试宝典里的,大多点到这为止,其实真实情况远远不是这样的。

如果持有以上观点,那么服务器上出现OOM,一点儿也不奇怪。了解Java进程的内存构成,对固定服务器内存下的JVM参数调优设置很有帮助。


Java程序耗费内存:

JVM内存占用=操作系统自身耗内存 + 堆 + Java永久代/元数据区/方法区/常量池/代码缓存 + 程序计数器(可忽略不计)*线程数 + 虚拟机进程本身 + 虚拟机栈(线程栈)*线程数 + 本地方法栈(JNI调用)*线程数 + 直接内存(Java NIO)


metaspace的组成
Klass Metaspace
开启压缩指针,Klass Metaspace就是用来存klass的,klass是我们熟知的class文件在jvm里的运行时数据结构-XX:CompressedClassSpaceSize=1g
NoKlass Metaspace
没有开启压缩指针,就不会有-XX:CompressedClassSpaceSize=1g这块内存,这种情况下klass都会存在NoKlass Metaspace里
NoKlass Metaspace专门来存klass相关的其他的内容,比如method,constantPool等

GC回收的实例对象:
1、虚拟机栈(栈帧中的本地变量表)中的引用的对象
2、方法区中的类静态属性引用的对象
3、方法区中的常量引用的对象
4、本地方法栈中的JNI(即一般说的Native方法)的引用的对象


GC回收的“无用的类”:
1、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
2、加载该类的 ClassLoader已经被回收
3、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法


内存溢出原因种类
1、堆溢出 jang.lang.OutOfMemoryError : Java heap space
原因:
1、堆的新生区或年老区没有足够的空间存放即将分配的内存
2、(不可回收内存+即将分配的内存)超过堆可用内存
改进办法:
1、检查堆内存泄露
2、合理设置堆的新生区和年老区比例
3、增大-Xmx


2、直接内存溢出(NIO、Unsafe包)java.lang.OutOfMemoryError
原因:
1、JDK中有一种基于通道(Channel)和缓冲区 (Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用。 由于直接内存受到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
2、-XX:MaxDirectMemorySize设置的很小,导致计算机内存不够用来继续分配直接内存
3、-Xmx设置的太小,且未设置-XX:MaxDirectMemorySize,导致计算机内存不够用来继续分配直接内存(-XX:MaxDirectMemorySize默认值跟-Xmx一样)
4、-Xms设置的太大,导致计算机没有足够的剩余内存给直接内存(也就是OS剩余内存,不足以分配够这个数值XX:MaxDirectMemorySize)
5、 直接内存不够申请新的内存空间时,会执行System.gc()触发Full GC,Full GC会将未被引用的对象及其指向的直接内存释放掉
有些NIO框架或者RMI,会手工调用System.gc()来触发Full GC
System.gc()的调用,需要注意正确设置参数:-XX:-DisableExplicitGC
    6、-XX:+DisableExplicitGC、-XX:+ExplicitGCInvokesConcurrent 、-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses 触发CMS、G1回收,CMS和G1都会回收新生代和老年代,如果CMS或者G1回收失败(堆内失败),会调用Full GC
    nio DirectByteBuffer对象在创建的时候关联了一个 PhantomReference,gc过程中如果发现某个对象除了只有PhantomReference引用它之外,并没有其他的地方引用它了,
    那将会把这个引用放到 java.lang.ref.Reference.pending队列里,在gc完毕的时候通知ReferenceHandler这个守护线程去执行一些后置处理,而DirectByteBuffer关联的PhantomReference是PhantomReference的一个子类,在最终的处理里会通过Unsafe的free接口来释放DirectByteBuffer对应的堆外内存块
详情:http://www.open-open.com/lib/view/open1431570358826.html
改进办法:
1、检查NIO、直接内存泄露,DirectByteBuffer类、Unsafe类. (真正分配内存的方法是unsafe.allocateMemory())
2、设置合理的值-XX:MaxDirectMemorySize(默认值跟-Xmx一样)
3、增大-Xmx,未设置-XX:MaxDirectMemorySize的情况下(-XX:MaxDirectMemorySize默认值跟-Xmx一样)
4、减小-Xmx,已手工设置大-XX:MaxDirectMemorySize的情况下(预留出更多的直接内存空间)
5、设置-XX:-DisableExplicitGC,设置可达的物理内存的值-XX:MaxDirectMemorySize
6、设置-XX:-DisableExplicitGC、-XX:-ExplicitGCInvokesConcurrent 、-XX:-ExplicitGCInvokesConcurrentAndUnloadsClasses


3、永久区溢出
原因:
1、永久区内存分配不足
jang.lang.OutOfMemoryError: PermGen space(jdk1.7)
jang.lang.OutOfMemoryError: Meta. space(jdk1.8)
2、jvm配置参数有-Xnoclassgc
改进办法:
1、减少系统需要的类的数量
2、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)
3、使用ClassLoader合理地装载各个类,并定期进行回收
4、去掉jvm配置参数-Xnoclassgc

4、虚拟机栈和本地方法栈溢出(过多线程)
原因:
1、栈深度太大,方法调用栈层级太深,一般是死循环或者递归函数的层级太深引起
2、单个栈容量太小,栈帧里放的大量的局部变量,超过单个线程的-Xss值
(每个线程分配到的栈容量越大,可用建立的线程数量自然就越少)
能创建的线程数:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一个进程的最大内存
JVMMemory         JVM内存
ReservedOsMemory  保留的操作系统内存
ThreadStackSize      线程栈的大小
改进办法:
1、减少线程栈空间-Xss 
2、减少线程总数
3、减少最大堆空间-Xmx

5、运行时常量池溢出
原因:
1、使用String.intern()这个Native方法,常量池分配在方法区PermGen space(jdk1.7)/Meta. space(jdk1.8)
2、jar包和class文件里定义定义的常量太多/太大
3、FULL GC之后,无法腾出足够的内存给即将要分配的常量池区对象
改进办法:
1、检查不合理的String.intern()调用
2、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)

6、方法区溢出
原因:
1、方法区存放Class相关信息,产生大量的类填满方法区
3、ASM、Cglib、javassit、动态代理库生成大量的类
4、大量的JSP文件编译成Servlet类文件
5、OSGI的应用,同一个类文件,被不同的加载器加载,也会视为不同的类
6、FULL GC之后,无法腾出足够的内存给即将要分配的方法区对象
7、jvm配置参数有-Xnoclassgc
改进办法:
1、增大-XX:PermSize -XX:MaxPermSize(jdk1.7)、增大-XX:MetaspaceSize -XX:MaxMetaspaceSize(jdk1.8)
2、检查类、类加载器导致内存泄漏
3、不需要的类及时卸载、回收,触发FULL GC回收PermGen space、Meta. space
4、去掉jvm配置参数-Xnoclassgc


7、GC效率低下
原因:
1、话在GC上的时间超过98%
2、老年代释放的内存小于2%
3、eden区释放的内存小于2%
4、连续5次都出现上述情况,抛出异常 GC overhead limit exceeded


8、tomcat多工程java.lang.OutOfMemoryError: PermGen space
原因:
1、tomcat部署的项目多
改进办法:
2、多工程共享jar包,修改tomcat/conf/catalina.properties
shared.loader=D:/soft/tomcat8-share-lib/*.jar
shared.loader=/home/dev/soft/tomcat8-share-lib/*.jar

TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

我的栏目

日历

« 2019-08-20  
    123
45678910
11121314151617
18192021222324
25262728293031

数据统计

  • 访问量: 20008
  • 日志数: 83
  • 建立时间: 2017-04-14
  • 更新时间: 2017-08-02

RSS订阅

Open Toolbar