一、虚拟机内存分布
1、程序计数器,当前线程执行字节码行号指示器,线程独占
2、java虚拟机栈,为虚拟机执行java方法(字节码)服务,一次方法调用对应一个栈桢,栈桢中包含局部变量表,存储基本类型和引用
3、本地方法栈,为执行native方法服务
4、java堆,线程共享,存放几乎所有实例和数组,垃圾收集器管理的主要区域
5、方法区(包含运行时常量池),线程共享,存放已加载类信息,常量,静态变量、即时编译器编译代码
6、直接内存,非jvm运行时数据区,也非jvm规范定义内存区,通过NIO操作

二、创建对象
1、类加载检查,new指令首先定位常量池类符号引用,并检查类是否被加载、解析及初始化,没有则执行类加载
2、分配内存,分配大小在内加载时确定,分配方式有指针碰撞(Bump the pointer)和空闲列表(Free List),取决于GC是否有压缩整理功能
3、本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),每个线程预先分配的内存,通过参数可设置
4、初始化零值,使实例对象可直接使用
5、对象必要设置(所属类、如何查找元数据、哈希码、GC分代信息等),存于对象头中(Object Header)
6、执行,初始化父类子类的变量、执行父类子类语句块、构造函数(类加载时已执行,处理静态变量和静态语句块)

三、对象内存布局
1、Header(对象头)、Instance Data(实例数据)和Padding(对齐填充)
2、Header包含Mark Word和指针类型,如果对象是数组还将有一块记录数组长度的信息
3、Mark Word 存储运行时数据,包括哈希码、GC分代年龄、锁状态、线程持有锁、偏向线程ID、偏向时间戳等
4、指针类型即指向对应类元数据的指针
5、Instance Data存储类定义的字段内容
6、Padding作为占位符存在,保证对象长度的8字节的整数倍

四、对象定位访问
1、对象定位访问有句柄和直接指针两种
2、句柄需要在堆中划一块句柄池,句柄中包括对象实例数据和类型数据的地址,引用指向的是句柄地址
3、句柄方式方便对象移动(垃圾收集时很频繁,只需移动句柄实例数据指针)
4、直接指针引用指向的是对象地址,对象内存中必须包含类型数据相关信息
5、直接指针好处是访问速度快(HotSpot 使用的是直接指针)

五、内存泄露溢出
1、程序计数器,唯一无内存溢出的区域
2、java虚拟机栈深度超长抛StackOverflowError(无限递归调用),虚拟机栈动态扩展超长抛OutOfMemoryError(很难实现)
3、本地方法栈同虚拟机栈一样,也会抛出StackOverflowError和OutOfMemoryError
4、java堆没有内存空间分配实例且无法扩展时,抛OutOfMemoryError(不断创建对象并插入List)
5、方法区动态生成大量类信息(cglib创建),无法满足内存分配时,抛OutOfMemoryError
6、jdk1.6字符常量保存于常量池,jdk1.7常量池只保存字符常量对应的引用,字符常量保存在堆中,所以字面量塞满常量池(String.intern)抛OutOfMemoryError,1.6分别是方法区溢出,1.7是堆溢出
5、直接内存不受java堆大小限制,受本机总内存限制,动态扩展失败抛OutOfMemoryError(反射Unsafe类分配内存或者NIO模拟)

内存溢出模拟代码

public static class OOMObject {}

	/**
	 * VM args -Xmx20M -Xms20M
	 */
	private static void doHeapOOM() {
		List<OOMObject> list = new ArrayList<OOMObject>();
		while (true) {
			list.add(new OOMObject());
		}
	}

	static int length = 0;

	/**
	 * VM args -Xss128k
	 */
	private static void doStackSOF() {
		try {
			stackLeak();
		} catch (Exception e) {
			System.out.println("stack length:" + length);
			throw e;
		}

	}

	private static void stackLeak() {
		length++;
		stackLeak();
	}
	
	/**
	 * VM args -XX:PermSize=10M -XX:MaxPermSize=10M
	 */
	private static void doMethodAreaOOM() {
		
		while(true) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(OOMObject.class);
			enhancer.setUseCache(false);
			enhancer.setCallback(new MethodInterceptor() {
				
				@Override
				public Object intercept(Object arg0, Method arg1, Object[] arg2,
						MethodProxy arg3) throws Throwable {
					return arg3.invokeSuper(arg0, arg2);
				}
			});
			enhancer.create();
		}
		
	}
	
	/**
	 * jdk1.6 VM args -XX:PermSize=10M -XX:MaxPermSize=10M
	 * jdk1.7 VM args -Xmx20M -Xms20M
	 */
	private static void doRuntimeConstantPoolOOM() {
		int i = 0;
		List<String> list = new ArrayList<String>();
		while(true) {
			list.add(String.valueOf(i++).intern());
		}
	}
	
	/**
	 * VM args VM args -Xmx20M -XX:MaxDirectMemorySize=10M
	 */
	private static void doDirectMemoryOOM() throws IllegalArgumentException, IllegalAccessException {
		Field field = Unsafe.class.getDeclaredFields()[0];
		field.setAccessible(true);
		Unsafe unsafe = (Unsafe) field.get(null);
		while(true) {
			unsafe.allocateMemory(1024*1024);
		}
		
	}