方法区
方法区与Java堆一样,都是各个线程共享的区域,其用来存储已被Java虚拟机加载的类型信息、常量和静态变量、即时编译器编译后的代码缓存等数据
同理,方法区无法满足新的内存分配需求时,也会抛出OOM异常
运行时常量池
方法区对于各种常量的存放是存放在运行时常量池里面的,也就是说,运行时常量池是方法区的一部分
对于Class文件中,除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池表,常量池表是用于存放在编译器生成的各种字面量与符号引用,这一部分的内容将在类加载后存放到方法区的运行时常量池中
运行时常量池是方法区的一部分,所以也会受到方法区的限制,当常量池无法再申请到内存时,就会抛出OOM异常
直接内存
直接内存并不是Java虚拟机运行时数据区域的一部分,但Java中会使用到这个内存,直接内存其实就是堆外内存,是本机的直接内存
在JDK1.4时,添加了一个NIO类(New Input/Output),引入了一种基于通道与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象来操控这部分内存,这样是可以提升性能的,因为避免了在Java堆和Native堆中来回复制数据,也就是不用维持一致性
这方面的内存不会受到Java堆的影响,但会受到本机总内存的影响,一样也会由于动态扩展申请内存失败而抛出OOM异常
我们在Java里面创建一个对象的时候,往往使用一个new关键字就完事了,当虚拟机遇到了一条new指令,其背后的过程大概如下
-
检查new指令的参数是否能在常量池中定位到一个类的符号引用(常量池存储着类的信息),并且去检查这个符号引用代表的类是否已经被加载、解析和初始化过
-
如果没有被加载过,或没有符号引用,那就会先去执行类的加载过程
-
类加载完毕后,接下来虚拟机就会为新生对象去分配内存(对象所需的内存大小在类加载完成后便可以完全确定了)
-
这里的操作其实就是在Java堆里面去划分内存去存储这个对象,但不同情况的Java堆划分内存的方式也不一样,主要有以下两种
-
指针碰撞:针对Java堆的内存是绝对规整的情况,即在Java堆中,所有被使用过的内存会被放在一边,空闲的内存则为另一边,那么中间只需使用一个指针来作为分界点的指示器,要分配多少内存就让该指针往空闲的内存方向移动即可,