java内存讲解 程序计数器: JVM是多线程的,每一个线程都有一个独立的程序计数器,程序计数器是一块较小的内存空间,它与线程共存亡,它保证线程切换后能恢复到正确的执行位置,JVM中的程序计数器指向的是正在执行的字节码地址,可以看作是当前线程所执行的字节码的行号指示器。
本地方法栈: Java可以通过java本地接口JNI(Java Native Interface)来调用其它语言编写(如C)的程序。
栈: Java栈是线程私有的,它的生命周期与线程相同。每个方法在执行的同时都会创建一个栈帧用于存储局部变量、操作数、操作数栈、动态链接、方法出口等信息。每一个方法的调用过程直至执行完成,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。它和上面那个栈的区别是 虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native(本地)方法服务。 在方法中定义的一些基本类型的变量和对象的引用变量都在栈内存中分配。 当定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 这就是为什么局部变量每一次都是一样的。就算给他加一后,下次执行函数的时候还是原来的样子。 栈的优势是,存取速度比堆要快,仅次于寄存器,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型 的变量(,int, short, long, byte, float, double, boolean, char)
堆: Java堆(Java Heap)是被所有线程共享的一块内存区域。在此内存区域中唯一目的就是存放对象实例(由new创建的对象、数组、类的成员变量),几乎所有的对象都在这里分配内存。堆里面存的东西用完之后靠垃圾回收机制不定期自动消除。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。 方法区: 它是虚拟机在加载类文件时,用于存放已加载的类的类信息,常量,静态变量,及编译后的代码(类方法)等数据的内存区域,是线程共享的。说白了就是保存类的模板
数组内存图 public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr);//[I@5f150435 } 以上方法执行,输出的结果是[I@5f150435,这个是什么呢?是数组在内存中的地址。new出来的内容,都是在堆内存中存储的,而方法中的变量arr保存的只是数组的地址。 输出arr[0],就会输出arr保存的内存地址中数组中0索引上的元素 两个变量指向一个数组 public static void main(String[] args) { // 定义数组,存储3个元素 int[] arr = new int[3]; //数组索引进行赋值 arr[0] = 5; arr[1] = 6; arr[2] = 7; //输出3个索引上的元素值 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); //定义数组变量arr2,将arr的地址赋值给arr2 int[] arr2 = arr; arr2[1] = 9; System.out.println(arr[1]); }
对象内存图 一个对象,调用一个方法内存图 程序从 main 方法中进入;运行到 Phone p 时,在栈中开辟了一个空间; new Phone() 时,在堆中开了一个内存空间,此时会有一个内存值;此时会找到对应的 Phone 的 class 文件,发现有三个变量和一个方法,于是将三个成员变量放在了堆中
通过上图,我们可以理解,在栈内存中运行的方法,遵循"先进后出,后进先出"的原则。变量p指向堆内存中 的空间,寻找方法信息,去执行该方法。但是,这里依然有问题存在。创建多个对象时,如果每个对象内部都保存一份方法信息,这就非常浪费内存了,因为所有对象的方法信息都是一样的。那么如何解决这个问题呢?请看如下图解。
两个对象,调用同一方法内存图
对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息 只保存一份,节约内存空间。 一个引用,作为参数传递到方法中内存图
引用类型作为参数,传递的是地址值。
