JVM2:内存溢出及常用命令列表

前言

本篇博文主要介绍使用程序触发对应的内存溢出,并附带上JVM常用的命令,供以后查看使用。

堆溢出

堆主要是用来存储对象,我们只要不断的创建对象,并防止虚拟机对对象进行回收则可以触发堆溢出。

  1. -Xms设置堆最小值、-Xmx设置堆最大值。如果两者相同,则可以避免堆自动扩展;
  2. -XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析
/**
 * Title:HeapOom 堆溢出
 * Description: VM args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 *
 * @author lin.xu
 * @date 2017/12/4.
 */
public class HeapOom {

  static class OomObject {

  }

  public static void main(String[] args) {
    List<OomObject> list = new ArrayList<OomObject>();

    while (true) {
      list.add(new OomObject());
    }
  }
}

执行过后,会提示java.lang.OutOfMemoryError: Java heap space

解决方法:

通过内存映像分析工具(如Eclipse Memory Analyzer、JProfiler)对Dump出来的堆转储快照进行分析,重点先确定是内存泄漏还是内存溢出。如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,掌握泄漏对象的类型信息及GC Roots引用链信息就可以定位泄漏代码位置;如果不是内存泄漏,则可以检查-Xms-Xmx参数设置,另外查看下代码判断对象生命周期是否过长。

虚拟机栈和本地方法栈溢出

栈内存容量的计算由操作系统为虚拟机分配的内存,扣除最大堆内存和方法区占用的最大内存之后所剩余的,由于程序计数器占用内存小可以忽略不计。

  1. -Xss设置虚拟机栈大小,-Xoss设置本地方法栈大小。但是对于HotSpot来说,-Xoss是无效的,因为HotSpot中根本不区分本地方法栈;
/**
 * Title:JavaVmStackSof 栈溢出
 * Description: -Xss180k
 *
 * @author lin.xu
 * @date 2017/12/4.
 */
public class JavaVmStackSof {
  private int stackLength = 1;

  public void stackLeak() {
    stackLength++;
    stackLeak();
  }

  public static void main(String[] args) {
    JavaVmStackSof jvss = new JavaVmStackSof();
    try {
      jvss.stackLeak();
    } catch (Throwable e) {
      System.out.println("Stack size: " + jvss.stackLength);
      throw e;
    }
  }
}

执行过后,会提示java.lang.StackOverflowError

解决方法:

一般可以依据抛出的异常直接定位到代码的位置。如果是由于过多线程导致的内存溢出,在不能减少线程数的情况下,只能减少最大堆和减少栈容量来换取更多的线程。

方法区和运行时常量池溢出

JDK1.7及之前版本,采用PermSizeMaxPermSize来设置方法区大小,由于JDK1.7开始已经在“去永久代”的实现,在JDK9中如果采用上述两个配置运行本节的示例则直接提示

Ignoring option PermSize; support was removed in 8.0。
Ignoring option MaxPermSize; support was removed in 8.0

JDK9中需要将PermSize替换为MetaspaceSizeMaxPermSize替换为MaxMetaspaceSize

方法区主要用于存放Class相关的信息,如类名、访问修饰符、常量池、字段描述及方法描述等。测试时主要就是加载大量的类来使方法区溢出。

/**
 * Title:JavaMethodAreaOom
 * Description:vm args: -XX:PermSize=10M -XX:MaxPermSize=10M
 *
 * @author lin.xu
 * @date 2017/12/5.
 */
public class JavaMethodAreaOom {

  public static void main(String[] args) {
    while (true) {
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(OomObject.class);
      enhancer.setUseCache(false);
      enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
            throws Throwable {
          return methodProxy.invokeSuper(o, objects);
        }
      });
      enhancer.create();
    }
  }
}

执行过后,会提示java.lang.OutOfMemoryError: PermGen space

本机直接内存溢出

DirectMemory容量可以通过-XX: MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值相同。

/**
 * Title:DirectMemoryOom
 * Description:vm args: -Xmx10M/-XX:MaxDirectMemorySize=10M
 *
 * @author lin.xu
 * @date 2017/12/5.
 */
public class DirectMemoryOom {
  private static final int _1MB = 1024 * 1024;

  public static void main(String[] args) throws IllegalAccessException {
    Field field = Unsafe.class.getDeclaredFields()[0];
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    while (true) {
      unsafe.allocateMemory(_1MB);
    }
  }
}

执行过后,会提示java.lang.OutOfMemoryError

由DirectMemory导致的内存溢出,明显的特征就是HeapDump文件中不会看见明显的异常。如果发现OOM之后Dump文件很小,而程序中又间接或直接使用了NIO,则可以考虑检查下是不是本机直接内存溢出问题。

JVM Args常用命令列表

命令说明
-Xms20M设置Java堆最小内存为20M,必须以M为单位
-Xmx20M设置Java堆最大内存为20M,必须以M为单位。如果值与-Xms设置的相同,则Java堆不能扩展
-Xss128k设置JVM栈内存大小为128K。在JDK9中,最小必须设置为180k
-Xoss128k设置本地方法栈大小为128k。不过在HotSpot虚拟机中,本参数无效。因为HotSpot中不区分JVM栈和本地方法栈
-XX:PermSize=10MJVM初始分配的永久代容量,必须以M为单位。JDK8过后已无效。
-XX:MaxPermSize=10MJVM允许分配的永久代最大容量,必须以M为单位。JDK8过后已无效
-Xnoclassgc关闭JVM对类的垃圾回收
-XX:+TraceClassLoading查看类的加载信息
-XX:+TraceClassUnLoading查看类的卸载信息
-XX:NewRatio=4年轻代:老年代=1:4
-XX:SurvivorRatio=82个Survivor:1个Eden=2:8
-Xmn20M年轻代大小为20M
-XX:+HeapDumpOnOutOfMemoryError程序抛出异常时,存储堆内存转储快照
-XX:+UseG1GCJVM使用G1垃圾收集器
-XX:+PrintGCDetails在控制台上打印出GC具体细节
-XX:+PrintGC在控制台上打印出GC信息
-XX:PretenureSizeThreshold=3145728对象大于3145728(3M)时直接进入老年代分配,这里只能以字节作为单位
-XX:MaxTenuringThreshold=1对象年龄大于1,则自动进入老年代
-XX:CompileThreshold=1000当一个方法被调用1000次之后,被认为是热点代码,触发即时编译
-XX:+PrintHeapAtGC查看每次GC前后堆内存布局
-XX:+/-UseTLAB虚拟机是否启动TLAB
-XX:+PrintTLAB查看TLAB的使用情况
-XX:+UseSpining开启自旋锁
-XX:+PreBlockSpin更改自旋锁的自旋次数,使用这个参数必须先开启自旋锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值