一.程序计数器
程序计数器其实就是一个程序所执行的字节码行号的指示器。通过这个指示器可以选取下一条要执行的字节码
为了线程上下文切换时可以恢复到以前的状态,所以每个线程都有一个私有的程序计数器,使得各个线程之间互不影响。
如果执行的是java方法就则计数器存储的是正在执行的字节码指令地址,如果是native方法则为空,因此程序计数器是唯一一块不会发生OOM的内存区域。
二.虚拟机栈
虚拟机栈也是线程私有的,所以有一种线程安全的策略就是利用栈隔离。
虚拟机栈用于存储局部变量表,操作数栈,动态链接和方法出口等信息。
一般程序员关注的是就是局部变量表。它存储的是各种基本数据类型,对象的引用(一种是对象的地址,一种是包含对象信息的句柄地址) 以及returnAddress类型。
如果申请的栈深度达到了虚拟机所允许的最大深度,那么会抛出stackOverflowError。比如无递归出口的递归函数。如果线程所申请的内存到达的虚拟机的最大内存会抛出OutOfMemoryError
三.本地方法栈
本地方法栈其实就是native函数库所使用的栈,本质和虚拟机栈一样。区别是虚拟机栈运行的是java方法,本地方法栈使用的语言跟寻实现虚拟机所用的语言。
和虚拟机栈一样也会发生stackOverflowError和OutOfMemoryError
四.java堆
堆内存主要是用来存放对象实例的,所有线程共享。具体可分为新生代和老年代。每一个线程都有一个分配缓存区(Thread Local Allocation Buffer,TLAB),用来保证分配内存时的线程安全
当虚拟机无法为对象分配内存时会抛出OutOfMemoryError
五.方法区
方法区属于线程共享的区域,用来存储已被加载的类信息,常量,静态变量以及被编译后的代码。
HotSpot虚拟机上把方法区重新定义为”永久代“,把GC的范围扩充至方法区,但是不好用,jdk1.7中已经把常量池从永久代中移出
同样方法区也会抛出OutOfMemoryError
六.运行时常量池
运行时常量池是方法区的一部分,每个class文件除了一些类版本,字段,方法,接口等描述信息外,还有常量池,常量池用于存储各种字面量和符号引用。当把class文件加载到方法区时会把class文件的常量池加载到运行时常量池中。
String.intern()方法就是把指定字符丢到常量池中。
运行时常量池属于方法区也会抛出OutOfMemoryError
七.直接内存
直接内存不属于java内存区域,属于堆外内存。
jdk1.4引入的NIO就是可以直接操作native函数库直接操作堆外内存,java堆中的DirectorByteBuffer对象直接引用了这块内存,避免了native堆到java堆的复制,有效的提高了IO的效率。