内存区域与内存溢出异常

第二章 内存区域与内存溢出异常

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 list=new ArrayList();
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.

posted @ 2018-11-27 11:51  李成洪  阅读(146)  评论(0)    收藏  举报