java堆
一.内存溢出的原因
1.堆溢出
当堆中强引用对象占用了大部分空间,导致内存无法回收,对象大小之和大于-Xmx就会导致堆溢出.
2.直接内存溢出
下面的代码申请直接内存,导致直接内存溢出 . 当JAVA进程的所有内存之和(堆空间,栈空间,直接内存,以及虚拟机自身所用的内存)大于-Xmx *2的时候,就会出现OOM.
-Xmx1g -XX:+PrintGCDetails (32位虚拟机,用户空间为2GB)
for(int i = 0 ;i< 1024;i++){
ByteBuffer b = ByteBuffer.allocateDirect(1024*1024);
}
3.过多线程导致OOM
-Xmx1g -XX:+PrintGCDetails
public static class OOMThread implements Runnable{
public void run(){
Thread.sleep(10000000);
}
}
public static void main(String[] args) {
for(int i = 0 ;i< 100000;i++){
new Thread(new OOMThread()).start();
}
}
有两种方法可以解决
1.减小堆空间大小 -Xmx. 2.减小每个线程所占用的线程空间 -Xss
4.永久去溢出
-XX:PermSize,然后不断的产生新类,用cglib
5.GC效率地下导致OOM
1.GC的时间超过98%.
2.老年代释放内存小于2%
3.eden区释放的内存小于2%
4.连续5次出现上诉条件.就会抛出 GC overhead limit exceeded
二. String 在虚拟机中的实现
1.不变性
一旦生成一个String对象,就不能进行改变.主要作用于多线程访问时, 省略同步的时间,提高性能.String的一些看起来是修改的操作,实质上都是通过新建字符串对象来实现的.
2.针对常量池的优化
当两个字符串拥有相同的值时,它们指向常量池的同一个拷贝.这样可以大量节省内存空间.
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.intern() == str2.intern());//true
3.String常量池的位置
在虚拟机中,有一块称为常量池的区间专门用于存放字符串常量.jdk1.6之前,这块区域属于永久区,jdk1.7之后被移动到堆中进行管理.
String.intern()方法返回常量池中的字符串,如果不存在就放入常量池.
下面的程序可以看出jdk1.6是在永久区,1.7之后是在堆中
jdk1.8:-Xmx5m -XX:MaxMetaspaceSize=5m -XX:-UseGCOverheadLimit
jdk1.6:-Xmx5m -XX:MaxPermSize=5m
jdk1.7:-Xmx5m -XX:MaxPermSize=5m
public static void main(String[] args) {
List<String> test = new ArrayList<String>();
for(int i = 0 ;i< 100000;i++){
test.add(String.valueOf(i).intern());
}
}
三.使用MAT分析java堆
打开eclipse中的Memory Analysis试图,用菜单File ->Acquire Heap Dump 选择要分析的堆.生成分析数据
这个试图里面可以观察到堆快照里类大小,类,实例,所有类的内存使用情况,线程以及线程内局部变量的信息.