Java 和 C++之间有一堵墙,墙里的人想出来,墙外的人想进去,这堵墙就是内存分配与垃圾回收(说实话,之前主要用c++,从来不想进去,在美国的一家小公司要用java,不进去也不行了)
1 内存区域的划分,java内存区域的划分远比c++要复杂的多, c++ 中也就分成: 栈 堆 代码区域 数据区域 静态全局区域(根据是否初始化还能再分成两类)
1.1 程序计数器:每个线程分配一个计数器,计数器中保存着当前线程运行的程序地址(可能是函数的静态下标,也可能是程序字节码地址)
1.2 Java虚拟机栈:存储局部变量,函数内声明的变量,int double string 指向对象的引用等等
1.3 本地方法栈:当java需要调用其他语言编写的程序时,需要使用关键字native修饰一个方法,这个方法称为本地方法,本地方法的地址存储在本地方法栈中
1.4 Java堆:划分为new generation 和old generation;
new generation 中保存是生命周期较短的小对象,在minor GC过程中就会被回收,new generation 将80%的内存划分为Eden区域, 另划分两个区域各自占10% 叫做survivor 区域。新创建出来的对象放在Eden中,GC后活下来的对象移动到survivor 中
old generation 长声明周期的对象
一次fullGC的过程:创建太多对象 ->Eden内存不足 -> 对象移动到old generation ->old 满了->full GC
1.5 方法区: 存储类信息 常量 静态变量,也叫permnent generation
1.6 运行时常量池:存放字面值与符号引用
1.7 直接内存:java虚拟机以外的内存,用于与外界进行交互
2 将每一块内存搞爆的方法:
1)将堆搞爆的方法:
List<String> li = new ArrayList<String>();
while(true) li.add(new String());
2) 将栈搞爆,递归函数地址存在栈中
class OOM {
private int c = 1;
public void cur() {
c++; cur();
}
main() {
cur();
}
}
3) 将常量区域搞爆:
List<String> li = new ArrayList<String>();
int i = 0;
while(true) li.add(String.valueOf(i++).intern());