内存区域与内存溢出异常
第二章 内存区域与内存溢出异常
2.2 运行时数据区域
2.2.1 程序计数器
程序计数器是当前程序执行的字节码的行号指示器,通过改变程序计数器的值可以实现 分支,跳转,循环,异常,线程回复等功能
### 2.2.2 Java虚拟机栈和本地方法栈
线程私有,与线程的生命周期相同。方法执行时在此处创建栈帧用于储存局部变量表,操作数栈,动态链接,方法出口的那个信息。
2.2.3本地方法栈
也是线程私有,与线程生命周期相同。它为执行本地方法提供服务
2.2.4 java堆
被所有线程共有,可以分为Eden ,From Survivor ,To Survivor .创建对象时在堆上分配内存,也是垃圾收集器管理的主要区域。
2.2.5方法区
所有线程共享,用于储存虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在HotSpot中也被称为永久代
2.2.6运行时常量池
它是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。运行期可以使用String.intern()方法使字符进入常量池
2.2.7直接内存
我觉得就是本机内存,没有什么特别的。
2.3对象
2.3.1对象的创建
先检查这个指令的参数是否能在常量池中定位到类的符号引用,并且检查符号引用的类是否被加载,解析,初始化,如果没有就去加载。然后分配内存,初始化为0值,设置元数据信息,对象的hash码,GC分代年龄,。
2.4实战
2.4.1 java 堆溢出
package memoryTest;
/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* @author 洪buff
*/
import java.util.ArrayList;
import java.util.List;
public class HeapMain {
static class OOMObject{
}
public static void main(String[] args){
List<OOMObject> list=new ArrayList<OOMObject>();
while(true){
list.add(new OOMObject());
}
}
}
2.4.2本地方法栈和虚拟机栈溢出
package memoryTest;
/**
* VM Args: -Xss128K
* @author 洪buff
*
*/
public class JavaVMStackSOF {
private int stackLength=1;
public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args)throws Throwable{
JavaVMStackSOF oom=new JavaVMStackSOF();
try{
oom.stackLeak();;
}catch (Throwable e) {
System.out.println("stack length:"+oom.stackLength);
throw e;
}
}
}
### 2.4.3本地方法栈溢出
package memoryTest;
/**
* VM Args:-Xss2M(或者更大)
* @author 洪buff
*
*/
public class JavaVMStackOOM {
private void dontStop(){
while(true){
}
}
public void stackLeakByThread() {
while (true){
Thread thread =new Thread(new Runnable(){
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom=new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
很危险,我不做演示
2.4.3方法区和运行时常量中intern
package memoryTest;
public class RuntimeConstanPoolOOM {
public static void main(String[] args) {
String str1=new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern()==str1);
String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern()==str2);
}
}
分析:StringBuilder在堆中创建字符串对象“计算机软件",intern()方法将它的引用复制到了字符串常量池,所以答案是true.
"java"第一次被创建是在类加载时,在常量池中加载,而str2则是指向堆中的”java"对象,intern()方法返回的时常量池中的“java"所以答案是false.