运行数据区的字节码只是存放在那里的一个二进制文件。要想在jvm中运行,首先要有运行的内存环境。也就是我们所说的jvm运行时数据区。1)运行时数据区的位置运行时数据区是jvm最重要的部分,也是执行引擎频繁操作的地方。类的初始化,以及我们后面要讲的对象空间的分配和垃圾回收,都发生在这个区域。2)区域划分按照《Java虚拟机规范》中的规定,运行时数据区被细分为几个线程私有的部分:Java虚拟机栈(JavaVirtualMachineStack)、程序计数器寄存器(ProgramCounterRegister)、本地方法栈(NativeMethodStacks)是大家共享的:MethodArea,JavaHeap。接下来我们将分块详细讲解,每个块是干什么的,溢出了会怎样1.1程序计数器1.1.1概述程序计数器(ProgramCounterRegister)是每个线程一个。是一小块内存空间,代表当前线程执行的字节码指令的地址。字节码解释器工作时,通过改变这个计数器的值来选择下一条要执行的字节码指令,所以整个程序的分支、循环、跳转、异常处理、线程恢复等基本功能都需要依赖于此。柜台完成。由于多个线程是并行执行的,执行的指令是不同的,所以每个线程都需要有一个独立的程序计数器。线程间的计数器互不影响,独立存储。我们称这种内存区域为“线程私有”内存。如果是native方法,这里为空1.1.2没有溢出异常!在虚拟机规范中,这个区域没有内存溢出规范,是唯一不会溢出的区域。1.1.3的情况,因为不会溢出,我们没有办法为它创建一个,只能从class类中创建。寻找痕迹。回顾上面javap的反汇编,代码对应的数字可以理解为计数器记录的执行次数。1.2虚拟机栈1.2.1概述也是线程私有的!生命周期与线程相同。它描述了Java方法执行的当前线程的内存模型。每个方法执行时,Java虚拟机都会同步创建一个栈帧,用于存放局部变量表、操作数栈、动态连接、方法出口等。每个方法被调用到执行完成的过程对应于一个栈帧在虚拟机栈中从入栈到出栈的过程。1.2.2溢出异常1)栈深度超过设置如果创建的栈深度大于虚拟机允许的深度,则抛出Exceptioninthread"main"java.lang.StackOverflowError2)内存申请不足如果栈允许内存扩展,但是当内存申请不够时,会抛出OutOfMemoryError。笔记!这个和具体的虚拟机有关。hotspot虚拟机不支持栈空间扩展,所以在单线程环境下,一个线程在创建时,会被分配一个固定大小的栈,它不会出现在这个固定的栈空间上。在扩展应用程序内存的情况下,不会出现应用程序不足的情况。上面的StackOverflowError只会是深度超出固定空间的问题导致的。但这与Xss栈空间的大小无关。就是因为线程太多,栈太多,导致系统分配给jvm进程的物理内存被吃光了。这时虚拟机自带相关提示:Exceptioninthread"main"java.lang.OutOfMemoryError:unabletocreatenativethreadssp:Eachthreadareallocated1Mspacebydefault(64-bitlinux,hotspotenvironment)问题:是否可以更改Xss?值可以得到堆栈空间溢出?答:根据上面的分析,热点是不允许的,仍然会抛出StackOverflowError,只是深度更小了。1.2.3案例一:进出栈顺序1)代码包com.iheima.jvm.demo;/***程序模拟入栈出栈的过程*先进后出*/publicclassStackInAndOut{/***定义方法1*/publicstaticvoidA(){System.out.println("进入方法A");}/***定义方法二;调用方法一*/publicstaticvoidB(){A();System.out.println("进入方法B");}publicstaticvoidmain(String[]args){B();System.out.println("进入Main方法");}}2)运行结果:进入方法A进入方法B进入Main方法3)栈结构:main方法---->B方法---->A方法1.2.4案例2:栈深度溢出1)The代码实现简单,方法可以自己嵌套:packagecom.iheima.jvm.demo;/***通过程序模拟线程请求的栈深度大于virtual允许的栈深度机器;*ThrowStackOverflowError*/publicclassStackOverFlow{/***定义方法,循环嵌套self*/publicstaticvoidB(){B();System.out.println("进入方法B");}publicstaticvoidmain(String[]args){B();System.out.println("进入Main方法");}}2)运行结果:Exceptioninthread"main"java.lang.StackOverflowErr或者在com.iheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)在com.iheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)在com.iheima.jvm.demo.StackOverFlow。B(StackOverFlow.java:12)在com.iheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)在com.iheima.jvm.demo.StackOverFlow.B(StackOverFlow.java:12)3)堆栈结构:1.2.5案例3:栈内存溢出如果一直创建线程,可以把栈填满但是!这个很危险,你可以在32位系统的winxp上试试,卡机概不负责!packagecom.iheima.jvm.demo;/**堆栈溢出,小心!非常危险,谨慎执行*执行时可能会卡死系统直到内存耗尽**/publicclassStackOutOfMem{publicstaticvoidmain(String[]args){while(true){newThread(()->{while(true);}).start();}}}1.3本地方法栈1.3.1概述本地方法栈的作用和特点与虚拟机栈类似,都具有线程隔离的特点。不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法。在虚拟机规范中,对这块使用的语言和数据结构没有强制规定。虚拟机可以自由实现。甚至,hotspot将它和虚拟机栈合并为一个1.3.2溢出异常和虚拟机栈一样,有两种:如果创建的栈深度大于虚拟机允许的深度,和内存申请不够的时候抛出一个StackOverFlowError,一个OutOfMemoryError1.4heap1.4.1概述和上面三个不同的是,heap是所有线程共享的!所谓的线程安全和不安全也是从这里来的。在虚拟机启动时创建。这块内存区的唯一用途就是存放对象实例,Java世界中“几乎”所有的对象实例都在这里分配内存。需要注意的是《Java虚拟机规范》并没有详细划分堆,所以堆的解释要根据具体的虚拟机。我们以最常用的HotSpot虚拟机为例。Java堆是垃圾收集器管理的内存区域,所以也称为“GC堆”,是JVM调优的重点区域。1.4.2jdk1.7jvm的内存模型在1.7和1.8有很大的不同。虽然1.7目前用的比较少,但是我们也需要了解1.7的内存模型,所以接下来先学习1.7再学习1.8的内存模型。Young区(代)Young区分为三部分,Eden区和两个大小相同的Survivor区。其中,在Survivor区,某个时间只使用其中一个,另一个留作垃圾回收。当对象在Eden区间被使用并变满时,GC会将存活的对象移至空闲的Survivor区间。根据JVM策略,经过几次垃圾回收后,Survivor对象会被移到下面的TheTenured区间。TenuredOldareaTenuredarea主要存放生命周期比较长的对象,一般是一些比较老的对象。当某些对象在Young中被复制和转移一定次数后,这些对象就会被转移到Tenured区域。一般情况下,如果系统使用了应用级Cache,缓存中的对象往往会被转移到这个范围内。Perm永久区域热点1.6只有这个产品,现在已经成为历史。Perm代主要存储类、方法和归档对象。这部分的空间一般不会溢出,除非一次加载很多类。但是,当涉及到热部署应用服务器时,有时会遇到java.lang.OutOfMemoryError:PermGenspace的错误。这个错误很大的原因可能是你每次都重新部署,但是重新部署之后,并没有卸载该类的类,这样一来perm中保存了大量的类对象。这种情况一般重启应用服务器即可解决。另一种可能是创建了大量的jsp文件,导致类信息超过perm上限而溢出。这次重新启动也没有解决它。你只能增加空间。虚拟区域:jvm参数可以设置一个范围,最大内存和初始内存的差就是虚拟区域。1.4.3jdk1.8从上图可以看出,jdk1.8的内存模型由两部分组成,年轻代+老年代。永久代被淘汰,取而代之的是Metaspace(元数据空间)年轻代:Eden+2*Survivor(不变)老年代:OldGen(不变)Metaspace:原来的perm区(重要!)需要特别说明Yes:占用的内存空间byMetaspace不在虚拟机内部,而是在本地内存空间,这是与1.7的永久代最大的区别。1.4.4内存不足时溢出异常,抛出java.lang.OutOfMemoryError:Javaheapspace1.4.5。案例:堆溢出1)代码分配了大量对象,超出了jvm指定的堆范围。包com.itheima.jvm.demo;importjava.util.ArrayList;importjava.util.List;/***堆溢出*-Xms20m-Xmx20m*/publicclassHeapOOM{Byte[]bytes=newByte[1024*1024];publicstaticvoidmain(String[]args){Listlist=newArrayList();诠释我=0;while(true){System.out.println(++i);list.add(新的HeapOOM());}}}2)start注意启动时,指定堆的大小:2)Output12345Exceptioninthread"main"java.lang.OutOfMemoryError:Javaheapspaceatcom.iheima.jvm.demo.HeapOOM.
