从几个sample来学习JAVA堆、方法区、JAVA栈和本地方法栈

最近在看《深入理解Java虚拟机》,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构:

感觉有必要通过几个实在的例子来加深对这几个区域的了解

1)Java堆

所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节,sample如下所示:

[java] view plaincopyprint?

  1. public class HeapOOM {
  2. static class OOMObject{}
  3. /**
  4. * @param args
  5. */
  6. public static void main(String[] args) {
  7. List<OOMObject> list = new ArrayList<OOMObject>();
  8. while(true){
  9. list.add(new OOMObject());
  10. }
  11. }
  12. }
public class HeapOOM {

	static class OOMObject{}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List<OOMObject> list = new ArrayList<OOMObject>();

		while(true){
			list.add(new OOMObject());
		}
	}

}

加上JVM参数-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError,就能很快报出OOM:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

并且能自动生成Dump。

2)方法区

方法区是存放虚拟机加载类的相关信息,如类、静态变量和常量,大小由-XX:PermSize和-XX:MaxPermSize来调节,类太多有可能撑爆永久带:

[java] view plaincopyprint?

  1. public class MethodAreaOOM {
  2. static class OOMOjbect{}
  3. /**
  4. * @param args
  5. */
  6. public static void main(String[] args) {
  7. // TODO Auto-generated method stub
  8. while(true){
  9. Enhancer eh = new Enhancer();
  10. eh.setSuperclass(OOMOjbect.class);
  11. eh.setUseCache(false);
  12. eh.setCallback(new MethodInterceptor(){
  13. @Override
  14. public Object intercept(Object arg0, Method arg1,
  15. Object[] arg2, MethodProxy arg3) throws Throwable {
  16. // TODO Auto-generated method stub
  17. return arg3.invokeSuper(arg0, arg2);
  18. }
  19. });
  20. eh.create();
  21. }
  22. }
  23. }
public class MethodAreaOOM {

	static class OOMOjbect{}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		while(true){
			Enhancer eh = new Enhancer();
			eh.setSuperclass(OOMOjbect.class);
			eh.setUseCache(false);
			eh.setCallback(new MethodInterceptor(){

				@Override
				public Object intercept(Object arg0, Method arg1,
						Object[] arg2, MethodProxy arg3) throws Throwable {
					// TODO Auto-generated method stub
					return arg3.invokeSuper(arg0, arg2);
				}

			});
			eh.create();
		}
	}

}

加上永久带的JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后会报如下异常:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

静态变量或常量也会有可能撑爆方法区:

[java] view plaincopyprint?

  1. public class ConstantOOM {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. // TODO Auto-generated method stub
  7. List<String> list = new ArrayList<String>();
  8. int i=0;
  9. while(true){
  10. list.add(String.valueOf(i++).intern());
  11. }
  12. }
  13. }
public class ConstantOOM {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> list = new ArrayList<String>();
		int i=0;
		while(true){
			list.add(String.valueOf(i++).intern());
		}
	}

}

同样加上JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后报如下异常:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

3)Java栈和本地方法栈

栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,栈大小由Xss来调节,方法调用层次太多会撑爆这个区域,samples如下所示:

[java] view plaincopyprint?

  1. package com.cutesource;
  2. public class StackOOM {
  3. /**
  4. * @param args
  5. */
  6. private int stackLength = 1;
  7. public void stackLeak(){
  8. stackLength++;
  9. stackLeak();
  10. }
  11. public static void main(String[] args) throws Throwable{
  12. // TODO Auto-generated method stub
  13. StackOOM oom = new StackOOM();
  14. try{
  15. oom.stackLeak();
  16. }catch(Throwable err){
  17. System.out.println("Stack length:" + oom.stackLength);
  18. throw err;
  19. }
  20. }
  21. }
package com.cutesource;

public class StackOOM {

	/**
	 * @param args
	 */

	private int stackLength = 1;

	public void stackLeak(){
		stackLength++;
		stackLeak();
	}

	public static void main(String[] args) throws Throwable{
		// TODO Auto-generated method stub
		StackOOM oom = new StackOOM();
		try{
			oom.stackLeak();
		}catch(Throwable err){
			System.out.println("Stack length:" + oom.stackLength);
			throw err;
		}

	}

}

设置JVM参数:-Xss128k,报出异常:

Exception in thread "main" java.lang.StackOverflowError

打印出Stack length:1007,这里可以看出,在我的机器上128k的栈容量能承载深度为1007的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:

[java] view plaincopyprint?

  1. package com.cutesource;
  2. public class StackOOM {
  3. /**
  4. * @param args
  5. */
  6. private int stackLength = 1;
  7. private void dontStop(){
  8. while(true){
  9. try{Thread.sleep(1000);}catch(Exception err){}
  10. }
  11. }
  12. public void stackLeakByThread(){
  13. while(true){
  14. Thread t = new Thread(new Runnable(){
  15. @Override
  16. public void run() {
  17. // TODO Auto-generated method stub
  18. dontStop();
  19. }
  20. });
  21. t.start();
  22. stackLength++;
  23. }
  24. }
  25. public static void main(String[] args) throws Throwable{
  26. // TODO Auto-generated method stub
  27. StackOOM oom = new StackOOM();
  28. try{
  29. oom.stackLeakByThread();
  30. }catch(Throwable err){
  31. System.out.println("Stack length:" + oom.stackLength);
  32. throw err;
  33. }
  34. }
  35. }
package com.cutesource;

public class StackOOM {

	/**
	 * @param args
	 */

	private int stackLength = 1;

	private void dontStop(){
		while(true){
			try{Thread.sleep(1000);}catch(Exception err){}
		}
	}

	public void stackLeakByThread(){
		while(true){
			Thread t = new Thread(new Runnable(){

				@Override
				public void run() {
					// TODO Auto-generated method stub
					dontStop();
				}

			});
			t.start();
			stackLength++;
		}
	}

	public static void main(String[] args) throws Throwable{
		// TODO Auto-generated method stub
		StackOOM oom = new StackOOM();
		try{
			oom.stackLeakByThread();
		}catch(Throwable err){
			System.out.println("Stack length:" + oom.stackLength);
			throw err;
		}

	}

}

报出异常:Exception in thread "main" java.lang.OutOfMemoryError:unable to create new native thread

不过在windows上运行这个例子要小心,会出现系统假死的情况,有可能需要重启机器才行。

以上几个例子虽然比较简单,但能很好帮助普通的程序员更加直观的了解Java堆方法区Java栈和本地方法栈

时间: 2025-01-15 01:08:24

从几个sample来学习JAVA堆、方法区、JAVA栈和本地方法栈的相关文章

JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区和堆

Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数据区域. 如下图所示: 一.程序计数器 Program Counter Register 定义:程序计数器是当前线程所执行字节码的行号指示器. 原因:Java 中的多线程是线程间轮流切换并需要 CPU 给予时间片的方式实现的.在任何一个确定的时刻,都只有一个线程在执行指令.为了线程间轮流切换后能够快

Java虚拟机OOM之虚拟机栈和本地方法栈溢出(4)

一.在 Java 虚拟机规范中,对虚拟机栈这个区域规定了两种异常状况: (1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常: (2)如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展,只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常. (3)与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和OutOfMemoryEr

Java虚拟机栈和本地方法栈

Java虚拟机栈的特征 线程私有 后进先出(LIFO)栈 存储栈帧,支持Java方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 Java本地方法栈的特征 线程私有 后进先出(LIFO)栈 作用是支撑Native方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 有一些虚拟机(如HotSpot)将Java虚拟机栈和本地方法栈合并实现 栈帧的概念和特征 Java虚拟机栈中存储的

java虚拟机 jvm java堆 方法区 java栈

java堆是java应用程序最密切的内存空间. 差点儿全部的对象都存在堆中.java堆全然自己主动化管理,通过垃圾回收机制,垃圾对象会自己主动清理.不须要显式释放. 依据java垃圾回收机制的不同.java堆可能有不同的结构. 最常见的是将整个java堆分为新生代和老年代.跟人类几乎相同了.老龄化就有可能找上帝去了,新生代存放新对象或者年龄不大的对象,老年代存放老年对象.新生代有可能分为eden区.s0区和s1区,s0区和s1区也被称之为 from 到to区域.他们是两块大小相等能够互换的内存空

第二章:Java虚拟机栈和本地方法栈溢出

由于在Hotspot虚拟机中中不区分虚拟机栈和本地方法栈,因此通过-Xoss修改参数是无效的,可以通过修改-Xss设定. 如果线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常. 这两种异常有一些重叠的部分:当栈空间无法继续分配时,到底是内存太小,还是已经使用的栈空间过大,其本质只是对同一件事情的两种不同描述. 可以通过一下方法验证: 在使用-Xss参数减少栈内存容量

java 堆 栈 方法区的简单分析

Java里的堆(heap)栈(stack)和方法区(method) 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 . 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收.局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收. 方法调用时传入的

Java堆

Java堆的概念 java堆的特征 全局共享 通常是Java虚拟机中最大的一块内存区域 作用是作为java对象的主要存储区域 JVMS明确要求该区域需要实现自动内存管理,即常说的GC,但并不限制采用哪种算法和技术去实现 可能出现OutOfMemory异常 java堆可能发生如下异常情况: 如果实际所需的堆超过了自动内存管理系统能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError异常. Java堆是出现内存异常概率最大的区域

&lt;&lt;深入Java虚拟机&gt;&gt;-第二章-Java内存区域-学习笔记

Java运行时内存区域 Java虚拟机在运行Java程序的时候会将它所管理的内存区域划分为多个不同的区域.每个区域都有自己的用途,创建以及销毁的时间.有的随着虚拟机的启动而存在,有的则是依赖用户线程来启动和销毁. 程序计数器 程序计数器是一块很小的区域,可以看做是用来表示线程所执行到字节码的某一行的行号指示器. 在Java虚拟机中,多线程是线程之间轮流切换并分配处理器的执行时间来实现的,为了线程切换之后能够继续回到之前的代码行继续执行,所以每个线程都有一个“线程私有”的程序计数器. 如果线程正在

Java自动内存管理机制学习(一):Java内存区域与内存溢出异常

备注:本文引用自<深入理解Java虚拟机第二版> 2.1 运行时数据区域 Java虚拟机在执行Java程序的过程中把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁.如下图所示: 2.1.1 程序计数器 程序计数器是一块较小的内存空间,它是线程的私有内存,可以看作时当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去