老生常谈JVM的内存溢出说明及参数调整攻略
什么是内存溢出?
在Java程序运行过程中,如果分配的堆内存不足以支撑当前应用程序的需求, JVM会抛出OutOfMemoryError,也就是我们常说的内存溢出。发生内存溢出后,通常有两种情况,一种是程序会直接退出,另一种则是程序变得非常缓慢,出现大量的Full GC以及OOM。
内存溢出的类型
内存溢出主要分为两类:
Java堆内存溢出
Java堆内存用于存储对象实例,当不断创建对象时,堆内存可能会被撑满,导致内存溢出。堆内存溢出时,可以通过调整JVM参数 -Xmx
和 -Xms
来增加JVM堆内存大小,从而避免该问题。
示例:
public class Main {
private static int i = 1000000;
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
while(true) {
list.add(i++);
}
}
}
使用默认堆内存参数执行该程序,会因为内存溢出而抛出java.lang.OutOfMemoryError: Java heap space
异常。
调整堆内存参数为-Xmx2g -Xms2g
后,重新执行程序,就不会出现内存溢出及异常。
方法区内存溢出
方法区存储的是类的相关信息以及静态变量等,当通过动态代理、反射等方式创建过多的类或者类的元数据时,可能会导致方法区内存溢出。方法区内存溢出时,可以通过调整JVM参数-XX:PermSize
和-XX:MaxPermSize
来增加方法区内存大小,从而避免该问题。
示例:
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
while(true) {
Proxy.newProxyInstance(
Main.class.getClassLoader(),
new Class<?>[] { MainInterface.class },
new MainHandler());
}
}
}
interface MainInterface {}
class MainHandler implements java.lang.reflect.InvocationHandler {
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
return 1;
}
}
使用默认方法区内存大小参数执行该程序,会因为内存溢出而抛出 java.lang.OutOfMemoryError: PermGen space
异常。
调整方法区内存大小参数为-XX:PermSize=128m -XX:MaxPermSize=256m
后,重新执行程序,就不会出现内存溢出及异常。
JVM调优
当应用程序出现内存溢出时,可以通过JVM参数调整来解决问题。下面列出几个常见的JVM参数调优:
堆内存大小参数
-
-Xms
: 设置JVM初始化堆内存大小,默认值为物理内存的1/64,当应用程序启动时即申请一块固定大小的内存,减少垃圾回收的时间; -
-Xmx
: 设置JVM最大堆内存大小,默认值为物理内存的1/4,当应用程序需要更多的内存时可以动态扩展,但当内存不足时可能会触发Full GC导致程序卡顿。
方法区内存大小参数
-
-XX:PermSize
: 设置JVM初始方法区内存大小,默认值为物理内存的1/64; -
-XX:MaxPermSize
: 设置JVM最大方法区内存大小,默认值为物理内存的1/4。
GC调优参数
-
-XX:+UseConcMarkSweepGC
: 开启并发垃圾回收器,以提高性能; -
-XX:+UseG1GC
: 开启G1垃圾回收器,可以更精细地控制对象的回收。
以上是一些JVM调优的常见参数,可以根据应用程序的实际情况调整。
总结
JVM的内存溢出是Java应用程序常见的问题,当出现内存溢出时,可以通过调整JVM参数来解决。常见的JVM参数调整包括堆内存大小、方法区内存大小和GC调优参数。当调整这些参数时需要根据应用程序的实际情况进行调整,可以通过实验确定最优参数值。
参考资料
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:老生常谈JVM的内存溢出说明及参数调整 - Python技术站