2.2 运行时数据区域
由所有线程共享的数据区:
- 方法区 Method Area
用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。
对于JDK中的class,JVM会在启动的时候把这些类加载到方法区中。
- 堆 Heap
几乎分配了所有对象实例和数组的内存,但是随着JIT 编译器的发展与逃逸技术逐渐成熟,这一说法并没有那么绝对。
垃圾收集器管理的主要区域,“GC堆”
线程隔离的数据区:
- 虚拟机栈 VM stack
生命周期同线程相同,每个Java方法在执行的时候,在虚拟机栈中新建栈帧,存储局部变量表、动态链接、操作数栈、方法出口。
局部变量表的大小在编译时唯一确定。
爆栈:StackOverFlowError 异常(线程请求深度大于虚拟机所需深度)
无法申请到足够的内存空间:OutOfMemoryError异常
- 本地方法栈 Native Method Stack
本地方法(Native Method )是指 一个java调用非java代码的接口,他是这样一个java的方法:该方法的实现由非java语言实现,比如C。
本地方法主要用来让java程序和操作系统和java环境外进行交互,可以用本地方法实现一些JVM没有封装的OS的系统调用的接口,使得java程序可以和操作系统的底层和硬件进行交互。
除此,本地方法栈和虚拟机栈的功能一致。
- 程序计数器 Program Counter Register
当前线程所执行的虚拟机字节码的行号指示器,可以理解为指令指针。
2.3 对象的创建
- new指令:
当虚拟机遇到new指令时,先看new后面的参数能否再常量池中找到对用的符号引用,但后看它对应的类有没有被加载、解析、初始化。没有的话,先执行类的加载(使用classloader将类从硬盘加载到方法区)。然后虚拟机再为new出来的对象分配内存(堆),这就等同于再堆中新划出来一块内存,这个内存的大小在类加载完之后是唯一确定的。如果堆内存绝对规整,空闲和占用分别在两端,则中间用一个指针指示分界线,新分配内存时,只需要将指针像空闲方向移动应距离,这种分配方式称为“指针碰撞”。如果堆内存不规整,则需要维护一个列表,该列表记录堆内存中哪些空间是可用的,这种分配方式称为“空闲列表”。
- 指针移动的线程安全问题:
因对象的创建和访问是比较频繁的,移动指针的操作不是线程安全的,解决方案:
- 对指针的移动使用同步锁,保证原子性。
- 为每个线程在堆中预先分配一块内存(本地线程分配缓冲TLAB),之后该线程需要分配的内存优先从TLAB中分配,当TLAB用完时,为该内存分配新的TLAB,这部操作需要同步锁。
- 初始化
内存分配完成后,虚拟机将分配到的内存空间都初始化为零值。
2.3.2 对象的内存布局
对象的哈希码: 哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。
在Java中,哈希码代表对象的特征。
例如对象 String str1 = “aa”, str1.hashCode= 3104
String str2 = “bb”, str2.hashCode= 3106
String str3 = “aa”, str3.hashCode= 3104
根据HashCode由此可得出str1!=str2,str1==str3
下面给出几个常用的哈希码的算法。
1、Object类的hashCode.返回对象的 [1] 内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。
2、String类的hashCode.根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串所在的堆空间相同,返回的哈希码也相同。
3、Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100),i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。
对象在内存中的存储布局可以分为:
对象头Header、实例数据Instance Data 和 对齐补充Padding
2.4 实战:OutOfMemoryError异常
首先设置JVM的最大堆内存,取20m,在IDEA中,点击右上角Edit Configuration设置,找到VM options,输入-Xms20m -Xmx20m表示最大最小堆内存均为20m。
编写Main.java:
import java.util.*;
public class Main {
static class OOMtest{
}
public static void main(String[] args) throws Exception{
List<OOMtest> list = new ArrayList<OOMtest>();
while(true){
list.add(new OOMtest());
}
}
}