Java内存管理

C++对象的生命周期由程序员负责维护的,程序员必须写代码delete自己创建的对象(new); 而Java对象的生命周期是由java虚拟机管理维护的,因此程序员不必写代码delete自己创建的对象.
1、java虚拟机运行时数据分区
Java内存管理_第1张图片

数据区 描述
程序计数器 内存空间较小,当前线程所执行的字节码的行号指示器,用于CPU切换线程前保存当前线程的执行状态;此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈 描述java方法执行的内存模型,存储局部变量表、操作数栈、动态链接、方法出口等信息
本地方法栈 描述java Natve方法(类似C中的库函数)执行的内存模型,有些虚拟机(如 Sun HotSpot)直接就把本地方法栈和虚拟机栈合二为一
内存空间较大,线程共享,存放对象实例;堆是垃圾收集器管理的主要区域。
方法区 线程共享,存储已被java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

2、Java对象访问
Java对象的访问涉及对栈、堆、方法区的访问。

Object obj = new Object();

在这句创建对象的代码中,obj 作为引用类型变量存储在java栈的本地变量表中;new Object()创建的实例则存储在栈中,同时Java堆还必须包含能找到对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,对象的类型数据存储在方法区。对象的实例数据(堆中)和类型数据的访问方式分两种:使用句柄和直接访问。
使用句柄:Java堆中会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对对象实例数据和类型数据各自具体地址信息。Java内存管理_第2张图片
直接访问:reference直接存放对象的堆地址,类型数据的地址存储在堆对象的内存布局当中。
Java内存管理_第3张图片
使用句柄访问的优势是reference中存储的是稳定的句柄地址,对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要被修改;使用直接指针访问方式的好处是速度快,节省了一次指针定位的开销。
3、OutOfMemoryError异常
OutOfMemoryError异常简称内存溢出,当Java虚拟机发现内存空间无法满足程序内存分配请求时,就会抛出此异常。Java堆、栈、方法区中都可能发生内存溢出。
3.1 堆溢出测试
在ecplise可通过预设置虚拟机的内存分区大小,来测试内存溢出错误,下图是测试Java堆内存溢出的虚拟机参数设置

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError

-Xms :堆最小容量
-Xmx:堆最大容量
-XX:+HeapDumpOnOutOfMemoryError:让虚拟机在出现内存溢出异常时Dump出当前内存堆转储快照以便进行事后分析,该内存快照文件.hprof位于工程目录。
Java内存管理_第4张图片

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

	}

}
class OMMObject{
}

在这里插入图片描述
Java内存管理_第5张图片
要想进一步分析内存溢出异常,需用ecplise Memoy Analyzer插件打开工程目录下的内存快照.hprof文件,关于Memory Analyzer插件的安装使用,参考内存分析工具 --Ecplise Memory Analyzer
3.2 栈溢出测试
java栈也称方法栈,调用递归方法时,很容易发生OutOfMemoryError或者StackOverflowError(栈过深),调试时,通过-Xss设置栈容量,让栈溢出更快发生。

-Xss:栈容量

/**
 * VM Args:-Xss128k
 * @author suisui
 *
 */
public class JavaVMStackSOF {
	private int stackLength = 1;
	
	public void stackLeak() {
		stackLength++;
		stackLeak();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		JavaVMStackSOF oom = new JavaVMStackSOF();
		try {
			oom.stackLeak();
		}catch(Throwable e) {
			System.out.println("stack length: "+oom.stackLength);
			throw e;
		}
	}

}

Java内存管理_第6张图片
3.3 运行时常量池溢出
运行时常量池储存的是运行期间产生的常量、字面量。如果运行时常量池内存不足,就会发生内存溢出。从jdk1.7开始,运行时常量池移动到了堆中,所以如果堆的内存不足,也会导致运行时常量池内存溢出。
-XX:PermSize=size 永生代最小容量
-XX:MaxPermSize=size 永生代最大容量

/**
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * @author suisui
 *
 */
public class RuntimeConstantPoolOOM {

	public static void main(String[] args) {
		// 使用List保持着常量池引用,避免Full GC回收常量池行为
		ArrayList<String> list = new ArrayList<String>();
		int i = 0;
		while(true) {
			/*intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。
			 *否则,在常量池中加入该对象,然后 返回引用。
			 */
			list.add(String.valueOf(i++).intern());
		}
		}

	}

参考:
Ecplise Memory Analyzer
JVM参数调优
JVM内存溢出分析

你可能感兴趣的:(Java虚拟机)