Java堆,方法区,Java栈和本地方法栈浅析

Wesley13
• 阅读 498

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

Java堆,方法区,Java栈和本地方法栈浅析

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

1)Java堆

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

[java]  view plain copy

  1. public class HeapOOM {

  2. static class OOMObject{}

  3. /**

  4. * @param args

  5. */

  6. public static void main(String[] args) {

  7. List list = new ArrayList();

  8. while(true){

  9. list.add(new OOMObject());

  10. }

  11. }

  12. }

加上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 plain copy

  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. }

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

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

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

[java]  view plain copy

  1. public class ConstantOOM {

  2. /**

  3. * @param args

  4. */

  5. public static void main(String[] args) {

  6. // TODO Auto-generated method stub

  7. List list = new ArrayList();

  8. int i=0;

  9. while(true){

  10. list.add(String.valueOf(i++).intern());

  11. }

  12. }

  13. }

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

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

3)Java栈和本地方法栈

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

[java]  view plain copy

  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. }

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

Exception in thread "main" java.lang.StackOverflowError

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

[java]  view plain copy

  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. }

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

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

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

点赞
收藏
评论区
推荐文章
待兔 待兔
2星期前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
红橙Darren 红橙Darren
3年前
C进阶 - 内存四驱模型
一.内存四驱模型不知我们是否有读过《深入理解java虚拟机》这本书,强烈推荐读一下。在java中我们将运行时数据,分为五个区域分别是:程序计数器,java虚拟机栈,本地方法栈,java堆,方法区。在c/c中我们将运行时数据,分为四个区域分别是:栈区,堆区,数据区,代码区。我们详细来介绍下:1.栈区:由编译器自动分配释放,存放函数的
Wesley13 Wesley13
2年前
java运行时数据区
运行时数据区包括以下几个部分:程序计数器,堆,java栈,本地方法栈,方法区1.程序计数器:当CPU需要执行指令时,需要从程序计数器中获取当前需要执行的指令所在存储单元的地址。用来指示执行哪条指令。其大小不会随程序的执行而发生改变。2.Java栈:java方法执行的内存模型。存放的时一个个栈帧,每个栈帧对应一个被调用的方法。  栈帧中包括:局
Wesley13 Wesley13
2年前
java的对象模型
java中的对象在内存中,究竟是怎样一种存在?这篇随笔,我们就来一探究竟。可能不够深入,但是我们把理解到位,深入到我们需要的程度这样即可。先来看下jvm的内存模型:程序计数器虚拟机栈和本地方法栈堆方法区(运行时常量池)!(https://oscimg.oschina.net/oscnet/3528c94568da5147907
Wesley13 Wesley13
2年前
Java 几种常见的OOM
Java虚拟机内存有好几个运行时数据区会有OOM的异常,如果能够区分根据报错区分出是哪些区域报出来的异常,会更便于定位问题,解决问题。1.Java堆溢出原因:由于不断创建对象实例,当对象数量达到了最大堆的容量限制后产生内存溢出异常。现象:java.lang.OutOfMemoryError:Javaheapspace解决方法:1)首
Stella981 Stella981
2年前
JVM内存模型和类加载机制
JVM内存模型Java代码是运行在Java虚拟机(JVM)上的,Java虚拟机通过解释执行(解释器)或编译执行(编译器)来完成。Java内存模型分为5个部分:方法区(MethodArea),Java堆(Heap),Java栈(VMStack),本地方法栈(NativeMethodStack),程序计数器(PC寄存器)!(ht
Stella981 Stella981
2年前
JVM内存区域划分
JVM内存区域划分一、JVM运行时数据区划分根据《Java虚拟机规范》JVM会把它管理的内存划分为若干个不同的数据区域,如下图所示:方法区、堆、栈(虚拟机栈、本地方法栈)、程序计数器。线程私有的意思是指,JVM每遇到一个新的线程就会为他们分配栈和程序计数器。!(https
Stella981 Stella981
2年前
JVM中的Stack和Frame
JVM执行Java程序时需要装载各种数据,比如类型信息(Class)、类型实例(Instance)、常量数据(Constant)、本地变量等。不同的数据存放在不同的内存区中,这些数据内存区称作“运行时数据区(RuntimeDataArea)”。运行时数据区有这样几个重要区:JVMStack(简称Stack或者虚拟机栈、线程栈、栈等),Frame(又称S
Stella981 Stella981
2年前
JVM类加载
运行时数据区java虚拟机定义了若干种程序运行时使用到的运行时数据区1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁2.第二种则是与线程一一对应,随线程的开始和结束而创建和销毁。java虚拟机所管理的内存将会包括以下几个运行时数据区域!(http://static.oschina.net/uplo
Wesley13 Wesley13
2年前
Java 虚拟机垃圾收集机制详解
本文摘自深入理解Java虚拟机第三版垃圾收集发生的区域之前我们介绍过Java内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程共存亡。栈中的每一个栈帧分配多少内存基本上在类结构确定下来时就已知,因此这几个区域的内存分配和回收都具有确定性,不需要考虑如何回收的问题,当方法结束或线程结