介绍的问题:
1.虚拟机中的内存是如何划分的?
2.具体分为哪些区域?
3.什么操作会导致内存溢出或异常?
1)这些区域中,有的区域会随着虚拟机进程的启动而存在,有些区域是以来用户线程的启动和结束而建立和销毁
名词解释:
1)程序计数器:是一块较小的内存空间,作用可以看做当前线程所执行的字节码的行号指示器。(注意:Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,一个内核一次只会执行一个线程中的指令,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,这个属于线程私有)
2)虚拟机栈:每个方法被执行时都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法从被调用到执行完成的过程就对应着一个栈帧在虚拟机中入栈到出栈的过程。(注意:虚拟机栈是线程私有的,它的生命周期与线程相同。)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;当扩展时无法申请到足够的内存是会抛出OutOfMemoryError异常。
3)本地方法栈:与Java虚拟机栈作用相似,虚拟机栈为执行Java方法服务,本地方法栈为虚拟机使用到的Native方法服务。抛出异常与虚拟机栈类似。
**4)Java堆:**Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。唯一作用就是存放对象实例。Java虚拟机规范描述是:所有的对象实例以及数组都要在堆上分配,但随着JIT编辑器的发展和逃逸技术的成熟,栈上分配、标量替换等优化技术使得分配方式不是唯一的。(逃逸技术:?? 标量分配:)Java堆页称GC堆,可以细分为新生代和老年代,再细分为Eden空间、From Survivor空间、To Survivor空间等。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
5)方法区:是各个线程共享的内存区域,作用是存储已经被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等数据。当该区域无法满足内存分配时,将会抛出OutOfMemoryError异常。
6)运行时常量池:方法区的一部分。要说运行时常量池,先了解一下文件常量池,即Class文件中的常量池,Class文件中除了保存类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
**7)直接内存:**nio中引入了一种基于通道与缓冲区的I/O方式,可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,显著的提高了性能。受本机内存的限制。
对象访问:主要访问方式是使用句柄和直接指针。
**句柄方式:**Java堆中将会划分出一块内存来作为句柄池,reference中存储的是对象的句柄地址,句柄中包含了对象实例数据和类型数据各自的具体地址信息。
**直接指针方式:**Java堆对象的布局中需要考虑如何放置类型数据的相关信息,reference中直接存储的就是对象地址。
两者比较:句柄方式最大的好处是reference中存储的是稳定的句柄地址,在对象移动是只会改变句柄中的实例数据指针,而reference本身不需要被修改;
直接指针访问方式最大的好处是速度更快.