
在上一篇文章中我们学习了内存和内存地址的基础概念。但Java程序运行时内存并不是“一锅粥”随便用而是被精心划分成不同的区域每个区域各司其职。你有没有遇到过这样的困惑定义两个基本类型变量调用方法交换值结果发现没变但是交换数组中的元素却可以成功。这是为什么答案就藏在Java的内存分配规则里。今天这篇文章我们就来彻底搞懂Java虚拟机运行时的内存分区包括栈内存、堆内存、方法区的核心作用并通过经典案例解释基本数据类型的参数传递特点。一、Java虚拟机内存五大区域Java程序运行时虚拟机会将占用的内存划分为五个区域区域名称作用掌握要求栈内存方法运行时使用的内存⭐⭐⭐ 核心掌握堆内存存储对象、数组new出来的东西⭐⭐⭐ 核心掌握方法区存储字节码信息、常量、静态变量⭐⭐⭐ 核心掌握本地方法栈调用本地native方法了解即可程序计数器记录线程执行的代码行号了解即可核心记忆规则为了便于记忆我把三个核心区域和它们的特征绑定在一起栈内存 → 和方法有关方法运行的地方堆内存 → 和new关键字有关看到new就在堆中方法区 → 和字节码信息有关存放.class文件内容二、三个核心内存区域详解1. 栈内存Stack特点每个线程拥有独立的栈空间方法被调用时入栈执行执行完毕后出栈遵循“先进后出”原则存储内容局部变量方法的参数方法的返回值执行过程程序主入口main本身也是一个方法由JVM启动时自动调用。调用后main方法入栈开始执行代码执行完毕后出栈从内存中释放。所有方法都遵循这个规则调用入栈执行完出栈。2. 堆内存Heap特点所有线程共享堆内存用来存储对象、数组、字符串等只要使用new关键字就一定会在堆内存中开辟新空间堆空间的特性每个通过new开辟的空间都自带一个独立的内存地址如10f87f48。这个地址可以被栈中的变量引用。核心判断规则看到new关键字就一定用到了堆内存。3. 方法区Method Area特点JDK7及以前由“永久代”实现JDK8开始由“元空间”实现移动到本地内存中所有线程共享存储内容编译后的字节码信息.class文件内容常量静态变量重要提醒方法区只负责存储不负责运行方法。方法要运行必须加载到栈中执行。不要混淆这两块空间的功能。三、案例一无new代码的内存分析代码示例public class MemoryDemo1 { public static void main(String[] args) { int a 10; int b 20; int c a b; System.out.println(c); } }问题这段代码用到了哪些内存区域分析有没有new关键字没有堆内存用到了吗没有堆内存只和new关键字相关。执行流程方法区程序启动将当前类的字节码文件MemoryDemo1.class加载到方法区记录类内所有信息栈内存JVM自动调用main方法main方法入栈栈中操作在main方法的栈空间中为变量a开辟空间存入10为变量b开辟空间存入20为变量c开辟空间计算ab得到30存入c输出结果打印c的值30方法出栈main方法执行完毕从栈中出栈所有局部变量随之释放正确答案只用到栈内存和方法区没有用到堆内存。四、案例二基本数据类型参数传递经典交换案例代码示例public class MemoryDemo2 { public static void main(String[] args) { int a 10; int b 20; System.out.println(交换前: a , b); // 10, 20 change(a, b); System.out.println(交换后: a , b); // 10, 20 ??? } public static void change(int a, int b) { int temp a; a b; b temp; } }运行结果交换前:10, 20 交换后:10, 20为什么没交换成功很多人会疑惑change方法里明明交换了a和b为什么main方法里的a和b没变答案就藏在栈内存的隔离性里。内存执行流程详解第一步main方法入栈程序启动将MemoryDemo2.class加载到方法区。JVM调用main方法main方法入栈。在main的栈空间中为变量a开辟空间存入10为变量b开辟空间存入20输出交换前结果10, 20第二步调用change方法调用change(a, b)时change方法被加载到栈中获得独立的栈空间。在change的栈空间中定义两个形参a和b将实参的值10和20复制一份赋值给形参此时内存中同时存在两组变量main方法栈a10, b20change方法栈a10, b20两个方法内的a、b位于不同的栈空间互不影响。第三步change方法内部交换执行交换逻辑int temp a; // temp 10 a b; // change的a 20 b temp; // change的b 10交换完成后change方法栈内a20, b10但这个交换只修改了change方法自己的变量对main方法栈中的变量毫无影响。第四步change方法出栈change方法执行完毕从栈中出栈它栈空间内的所有变量包括交换后的a、b随之销毁。第五步回到main方法程序回到main方法继续执行输出main方法中原本的a和b——仍然是10和20。核心结论基本数据类型的变量存储的是真实数据本身参数传递传递的是真实数据的值副本。形参拿到的是实参的副本形参如何修改都不会影响原来的实参这就是Java的值传递机制五、内存分区核心考点总结内存区域绑定规则存储内容特性栈内存方法局部变量、方法参数线程私有方法入栈出栈堆内存new关键字对象、数组线程共享有内存地址方法区字节码.class信息、常量、静态变量线程共享只存不运行基本数据类型值传递口诀基本类型存数值参数传递给副本形参随便怎么改实参稳坐钓鱼台。六、写在最后Java的内存分配是理解后续所有知识的基础包括数组、对象、集合框架等。下篇文章我们将通过数组案例学习引用数据类型的参数传递特点看看为什么交换数组元素就能成功。敬请期待