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

    xiaoxiao2021-03-25  39

    很多人的理解是,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
    转载请注明原文地址: https://ju.6miu.com/read-50354.html

    最新回复(0)