JVM内存模型
++共享内存模型来实现多线程之间的信息交换和数据同步的。++
方法区和堆属于线程共享内存区,虚拟机栈,本地方法栈和程序计数器数据线程私有内存区。
程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,这在某种程度上有点类似于“ThreadLocal”,是线程安全的。
Java 虚拟机栈
与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,
它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame ) 用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress 类型(指向了一条字节码指令的地址)。
本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛StackOverflowError 和OutOfMemoryError异常。
Java堆
对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。jvm堆分为新生代(一般是一个Eden区,两个Survivor区)和老年代(old区)
方法区
方法区主要保存的是类的元数据。与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量池、静态变量、即时编译器编译后的代码等数据。
类型信息包括类的完整名称,父类的完整名称,类型修饰符和类型的直接接口类表。
常量池(permGen) 包括类方法,域等信息所引用的常量信息。
运行时常量池中的常量,基本来源于各个class文件中的常量池。
程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。
注:Java7中,字符串常量池从方法区移到了堆中;
Java8中,整个常量池从方法区中移除,方法区使用的是元空间(MetaSpace)实现。
JVM内存参数配置
-Xmx:最大堆大小
-Xms:初始堆大小(最小内存值)
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中的Eden区与Survivor区的大小比值
栈与堆
栈:存储局部变量,所属作用域一旦结束,该变量自动释放;
堆:存储数组(这就是为什么数组元素有初始值的原因)(数组也是对象)和对象,即new 申请的内存空间都存放在堆里;
栈和堆特点: 1)每个实部都有首地址值
2)堆内存每个变量都有默认值,而栈中无
3)垃圾回收机制
注:默认值int ~ 0,float ~ 0.0F,
double ~ 0.0D,
boolean ~false,char ~ ‘\u000’(空)’;
long~0
对象的创建
描述Java中一个对象创建的具体过程。
Object object = new Object();
上面语句实现了三个操作,“对象引用的创建”“对象的创建”“引用地址指向堆中内存空间”
1. 对象引用的创建
Object object;//创建了一个Object对象的引用,object本身只是一个地址值,并未在堆中创建一个Object对象。
2. 对象的创建
new Object();
a. 首先会检测这个指令的参数是否能在常量池中定位到一个类符号的引用,并且检测这个符号的引用代表的类是否已经被加载、连接、初始化。
(如果没有则执行相应的类加载过程)。
b. 虚拟机会在堆中分配内存空间,并且将内存空间都初始化为零值。
c. 进行程序员初始化操作,即调用构造方法。
3. 引用地址指向堆中内存空间
对象创建完毕后,将Object对象引用指向刚才创建的堆中的空间。
创建对象的方式中,那些会调用构造方法?
1. new创建,会调用构造方法
2. java反射机制使用java.lang.Class或者java.lang.reflect.Constructor的newInstance()方法
使用对象的clone();方法不会调用对象的构造方法。
内存溢出异常
- java.lang.OutOfMemoryError: PermGen space
“永久代”内存大小不足,“永久代”的解释应该为JVM中的方法区,主要用于存储类信息,常量,静态变量,即时编译器编译后代码等。
本错误仅限于Hotspot虚拟机,本区进行垃圾回收很少,不够直接加大简单粗暴。
- java.lang.OutOfMemoryError: Requested array size exceeds VM limit
直接翻译报错信息:数组过长导致堆内存溢出,加大堆内存或者减少数组长度。
- java.lang.OutOfMemoryError: Java heap space
堆内存不足,直接增大堆内存。
OutOfMemoryError异常
程序计数器:无
Java虚拟机栈: 如果虚拟机栈可扩展,扩展时无法申请到足够内存
本地方法栈:与Java虚拟机栈相同
Java堆:堆中没有内存完成实例分配,并且堆无法再进行扩展
方法区(运行时常量池):方法区无法满足内存分配需求(常量池无法申请到内存)
直接内存:内存区域总和大于物理内存总和
StackOverflowError异常
程序计数器:无
Java虚拟机栈:线程请求的栈深度大于虚拟机所允许的深度
本地方法栈:与Java虚拟机栈相同
Java堆:无
方法区:无
直接内存:无