一.本地方法栈
Nativemethodstack(本地方法栈):保存native方法进入区域的地址
对于一个运行中的Java程序而言,它还可能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,但不止如此,它还可以做任何它想做的事情。
任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。
如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。
这幅图展示了JAVA虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作它的Java栈;或者它可能毫无障碍地在Java栈和本地方法栈之间跳转。
该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。假设这是一个C语言栈,其间有两个C函数,第一个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过本地方法接口回调了一个Java方法(第三个Java方法),最终这个Java方法又调用了一个Java方法(它成为图中的当前方法)。
二.堆
2.1概念
Heap 堆
- 通过 new 关键字创建对象都会使用堆内存
- 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。它是 JVM 管理的最大一块内存空间。
特点
-
它是线程共享的,堆中对象都需要考虑线程安全的问题
《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。所有的线程共享Java堆,在堆中还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。
-
有垃圾回收机制(后续在第二个系列:垃圾回收机制深入讲解)
说明:
当栈帧被执行的时候,里面有对象的创建,那么栈帧里面仅仅是保存对象名以及对应的地址值,真正的对象存储是分配在了堆内存:(全流程图)
2.2内存分配关系
《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。(The heap is the run-time data area from which memory for all class instances and arrays is allocated)
要注意的是:“几乎”所有的对象实例都在这里分配内存——是从实际使用角度看的。因为还有一些对象是在栈上分配的。
数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。
比如下面一段很简单的代码:
public class Demo2 {
public static void main(String[] args) {
Hello h1 = new Hello();
Hello h2 = new Hello();
}
}
class Hello{
}
在内存中的存放位置如下:
在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除,也就是触发了GC的时候,才会进行回收。如果堆中对象马上被回收,那么用户线程就会受到影响。
堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。
2.3设置堆内存大小与OOM
Java堆区用于存储Java对象实例,那么堆的大小在JVM启动时就已经设定好了,大家可以通过选项"-Xmx"和"-Xms"来进行设置。
“-Xms"用于表示堆区的起始内存,等价于-XX:InitialHeapSize。
“-Xmx"用于表示堆区的最大内存,等价于-XX:MaxHeapSize。
一旦堆区中的内存大小超过“-Xmx"所指定的最大内存时,将会抛出OutOfMemoryError异常。
通常会将-Xms和-Xmx两个参数配置相同的值,其目的是 为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能 。
默认情况下:
初始内存大小:电脑物理内存大小/64。
最大内存大小:电脑物理内存大小/4。
可以通过如下代码进行查看:
/**
* -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
* -X:是jvm运行参数
* ms:memory start
* -Xmx:用来设置堆空间(年轻代+老年代)的最大内存大小
*/
public class Demo3 {
public static void main(String[] args) {
// 返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
// 返回Java虚拟机试图使用的最大堆内存
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms:" + initialMemory + "M");
System.out.println("-Xmx:" + maxMemory + "M");
}
}
运行结果:
-Xms:245M
-Xmx:3625M