JVM深入浅出(二)--了解JVM内存溢出

来源:互联网 发布:虚拟机优化显卡性能 编辑:程序博客网 时间:2024/05/21 12:41

JVM内存溢出大概分为以下几种情况:
1.内存中加载的数据量过大。
 比如一次性从数据库加载过多的数据。
2.并发数量太高。
 并发数量太高,导致在短时间内创建大量的对象,GC也不及回收。
public class HeapOOM {

public class NewObjectTest {  static class ObjectTest {  }  public static void main(String[] args) {      List<ObjectTest > list = new ArrayList<ObjectTest >();      while (true) {          list.add(new ObjectTest ());      }  }  

}
程序输出:
/**
* java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1820.hprof …
Heap dump file created [24787111 bytes in 0.346 secs]
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at com.jacx.NewObjectTest .main(HeapOOM.java:19) `
*/
这是由于集合类中有无用对象的引用,使用完后没有立即清除。
 集合类中的对象,如果不手动进行清除,GC不是不会对集合中无用的对象进行回收。
同事代码中存在死循环,递归,或者循环次数过多产生大量的对象。也会导致内存溢出

//线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError。public class JavaVMStackOOM {      int i=0;         private void dontStop() {                while (true) {                }         }         public void stackLeakByThread() {                while (true) {                       Thread thread = new Thread(new Runnable() {                              @Override                              public void run() {                                     dontStop();                              }                       });                       i++;                       System.out.println("i="+i);                       thread.start();                }         }         public static void main(String[] args) throws Throwable {                JavaVMStackOOM oom = new JavaVMStackOOM();                try {                    oom.stackLeakByThread();              } catch (Throwable e) {                  System.out.println("thread num:" + oom.i);                  throw e;              }         }  } 

3.直接内存溢出
虽然使用DerictByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是unsafe.allocateMemorypublic class DirectMemoryOOM {

private static final int _1MB = 1024 * 1024;  public static void main(String[] args) throws Exception {      Field unsafeField = Unsafe.class.getDeclaredFields()[0];      unsafeField.setAccessible(true);      Unsafe unsafe = (Unsafe) unsafeField.get(null);      while (true) {          unsafe.allocateMemory(_1MB);      }  }  

} 法区内存溢出。
 方法区存放的是Class类型信息,类名,常量池,修饰符,方法描述等信息。
 使用了过多的静态变量。常量池也被大量的占用。
 jvm在“运行期间” 产生了大量的类。导致填满了方法区。比如使用反射,动态代理,字节码生成技术会在运行期间产生大量的类和类型信息。如hibernate,spring第三方框架大量使用了cglib技术产生大量的动态类。
 大量的jsp在编译生成java类时也有可能产生方法区溢出,GC对方法区的回收非常苛刻的,因为对于一个类的回收条件就很严格。
 总而言之:当运行时常量池过大或者类过多时就会导致方法区溢出。

     public static void main(String[] args) {          // 使用List保持着常量池中对象的引用,避免JAVA回收机制回收常量池行为          List<String> list = new ArrayList<String>();           int i = 0;           while (true) {              list.add(String.valueOf(i++).intern());          }      }  
//循环实现方式 public static void main(String[] args) {          while (true) {              Enhancer enhancer = new Enhancer();              enhancer.setSuperclass(OOMObject.class);              enhancer.setUseCache(false);              enhancer.setCallback(new MethodInterceptor() {                  @Override                  public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {                      // TODO Auto-generated method stub                      return proxy.invokeSuper(obj, arg);                  }              });              enhancer.create();          }      }      static class OOMObject {      }  

5.启动时 JVM 内存参数设置过小。这个改变-Xms -Xmx 属性就可以了
 

0 0
原创粉丝点击