当前位置: 首页 > news >正文

Java基础知识总结(二):JVM内存结构与变量生命周期

Java基础知识总结(二):JVM内存结构与变量生命周期

很多Java开发者刚开始学习时都会有这样的疑问:

  • 对象到底存在哪里?
  • new出来的对象什么时候被回收?
  • 局部变量和成员变量有什么区别?
  • static变量到底存放在哪里?

这些问题都与 JVM 内存结构密切相关。

本文将从 JVM 运行时数据区出发,深入理解 Java 程序运行过程中内存是如何分配和管理的。


目录

  • JVM内存结构概述
  • 程序计数器
  • Java虚拟机栈
  • 本地方法栈
  • 堆内存
  • 方法区
  • 对象创建过程
  • 成员变量与局部变量区别
  • static变量存储位置
  • 生命周期分析
  • 常见面试题
  • 总结

一、JVM内存结构概述

Java程序运行时,JVM会将内存划分为多个区域。

主要包括:

┌──────────────┐ │ 程序计数器 │ ├──────────────┤ │ Java虚拟机栈 │ ├──────────────┤ │ 本地方法栈 │ ├──────────────┤ │ 堆 │ ├──────────────┤ │ 方法区 │ └──────────────┘

通常面试中提到的JVM内存结构,指的就是这些运行时数据区。


二、程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间。

作用:

记录当前线程正在执行的字节码指令地址。

简单理解:

程序执行到哪一行代码了 程序计数器负责记录

例如:

publicstaticvoidmain(String[]args){inta=10;intb=20;intc=a+b;System.out.println(c);}

JVM执行时:

执行第一句 记录位置 执行第二句 更新位置 执行第三句 更新位置

程序计数器就是一个“导航仪”。


为什么需要程序计数器?

Java支持:

  • 多线程
  • 线程切换

例如:

线程A执行一半 CPU切换到线程B 线程B执行完 再切换回线程A

程序计数器能够帮助线程恢复到之前执行的位置。


特点

程序计数器:

线程私有

每个线程都有自己的程序计数器。

也是 JVM 唯一不会发生 OutOfMemoryError 的区域。


三、Java虚拟机栈(Stack)

栈是面试中最常见的知识点之一。

作用:

保存方法运行时产生的数据。


方法调用过程

例如:

publicstaticvoidmain(String[]args){test();}publicstaticvoidtest(){intnum=10;}

执行过程:

main() 入栈 ↓ test() 入栈 ↓ test()执行结束 ↓ test()出栈 ↓ main()结束 ↓ main()出栈

图示:

┌───────┐ │ test │ ├───────┤ │ main │ └───────┘

栈中存放什么?

主要存放:

局部变量 方法参数 方法调用信息 返回地址

例如:

publicvoidtest(){inta=10;Stringname="Tom";}

变量:

a name

都存放在栈中。


栈的特点

先进后出(FILO)

Last In First Out

后进入的方法先执行完毕。


四、本地方法栈(Native Method Stack)

本地方法栈专门服务于:

native

修饰的方法。

例如:

publicnativevoidstart0();

JDK底层很多功能都依赖native方法实现。

例如:

  • 操作系统交互
  • 文件系统
  • 网络通信
  • 硬件访问

为什么需要本地方法?

Java本身无法直接操作:

CPU 内存 磁盘 网卡

因此需要借助:

C C++

编写的本地方法实现。


五、堆(Heap)

堆是 JVM 中最大的一块内存区域。

作用:

存储对象和数组。


对象创建过程

例如:

Studentstu=newStudent();

执行时:

第一步

栈中创建引用变量

stu

第二步

堆中创建对象

Student对象

第三步

引用指向对象

stu → Student对象

图示:

栈(Stack) stu │ ▼ 堆(Heap) Student对象

数组也存放在堆中

例如:

int[]nums={1,2,3};

图示:

栈 nums │ ▼ 堆 [1,2,3]

堆中的默认值

对象创建后会自动初始化。

例如:

classUser{intage;booleanflag;}

创建对象:

Useruser=newUser();

默认值:

age=0flag=false

为什么堆需要GC?

因为:

newUser();newUser();newUser();

会不断创建对象。

如果不回收:

内存耗尽

因此 JVM 提供:

Garbage Collection

垃圾回收机制。


六、方法区(Method Area)

方法区用于保存:

类信息 方法信息 常量 静态变量 运行时常量池

例如:

publicclassUser{privateStringname;publicvoidlogin(){}}

类加载后:

User类的信息 login方法的信息

都会进入方法区。


方法区的发展

JDK6

永久代(PermGen)

属于方法区实现。


JDK8

永久代被移除。

改为:

元空间(MetaSpace)

使用本地内存。


七、对象创建全过程

例如:

Useruser=newUser();

执行过程:

1. 类加载

如果User类未加载:

加载User.class

进入方法区。


2. 分配内存

在堆中创建对象。


3. 默认初始化

例如:

intage;

初始化为:

0

4. 调用构造方法

publicUser(){}

执行构造方法。


5. 返回对象地址

引用变量保存地址。

user → 对象

八、成员变量与局部变量区别

这是面试高频题。


定义位置不同

成员变量:

publicclassUser{intage;}

定义在类中、方法外。


局部变量:

publicvoidtest(){intage=18;}

定义在方法内部。


默认值不同

成员变量:

有默认值。

例如:

intage;

默认:

0

局部变量:

没有默认值。

必须先赋值再使用。

错误示例:

publicvoidtest(){intage;System.out.println(age);}

编译失败。


作用域不同

成员变量:

整个对象

都可以访问。


局部变量:

仅当前方法

有效。


存储位置不同

成员变量:

跟随对象存在。


局部变量:

跟随方法存在。


生命周期不同

成员变量:

对象创建 ↓ 对象销毁

局部变量:

方法开始 ↓ 方法结束

对比表

对比项成员变量局部变量
定义位置类中方法中
默认值
存储位置
生命周期对象生命周期方法生命周期
作用域整个类当前方法

九、static变量存储位置

很多初学者认为:

static变量在堆中

实际上需要区分JDK版本。


JDK6

永久代(方法区)

JDK8+

元空间保存类信息 静态变量实际存储在堆中

因此现在更准确的说法:

静态变量属于类 生命周期与类一致 对象共享同一份静态变量

示例:

publicclassUser{publicstaticintcount=0;}

无论创建多少对象:

newUser();newUser();newUser();

都共享:

count

十、常见面试题

面试题1

对象存放在哪里?

答案:

堆(Heap)

面试题2

局部变量存放在哪里?

答案:

Java虚拟机栈

面试题3

数组存放在哪里?

答案:


面试题4

方法调用为什么要入栈?

答案:

保存方法运行状态 记录局部变量 记录返回地址

面试题5

成员变量和局部变量有什么区别?

答案:

定义位置不同 默认值不同 作用域不同 生命周期不同 存储位置不同

面试题6

为什么Java需要垃圾回收?

答案:

对象不断创建 如果不回收 最终导致内存耗尽

总结

本文介绍了 JVM 运行时数据区的重要组成部分:

  • 程序计数器
  • Java虚拟机栈
  • 本地方法栈
  • 方法区

同时分析了:

  • 对象创建过程
  • 成员变量与局部变量区别
  • static变量存储位置
  • 生命周期差异

掌握这些知识后,你将能够更好地理解:

  • 对象为什么会被回收
  • 内存溢出的原因
  • JVM性能调优基础
  • Java面试中的内存相关问题
http://www.jsqmd.com/news/977407/

相关文章:

  • 2026 绍兴防水补漏服务商口碑测评榜单|全屋渗漏维修机构优选指南 - 宅安选房屋修缮
  • ComfyUI-FramePackWrapper:8GB显存实现高质量AI视频生成的完整指南
  • LPC55(S)xx硬件设计实战:PCB层叠、电源完整性与VBAT斜坡要求详解
  • 零基础学 ArkUI24:手把手教你开发一个简易浏览器 App
  • 【倒摆控制】三重倒摆控制项目(采用噪声和卡尔曼滤波技术)附Matlab实现
  • 互关原则
  • 3分钟搞定B站全量评论爬取:零代码获取10万+评论的完整解决方案
  • 一篇读懂薛定谔定律:从微观宇宙到人生启示
  • Midscene.js:AI驱动的跨平台UI自动化革命
  • PrivateGPT 1.0:构建企业级私有AI应用的开源API层
  • 2026推荐:广州双极真空泵维修服务公司专业精修与高效服务之选 - 企业推荐官【官方】
  • Zotero-GPT插件API调用故障排查:3步解决AI功能失效问题
  • 2026年GEO优化服务商可靠性综合评估报告:数据驱动下的专业选型指南 - GEO优化
  • 人力资源数据分析实用指南:HR新人同事必读
  • 【飞机】基于数据驱动的多传感器飞机健康监测系统附Matlab代码
  • LPC845 I2C SBL实战:嵌入式固件远程更新与内存布局解析
  • LLM —— Prompt提示词工程
  • GoLiveChat:Golang独立部署海外英文在线客服系统全解析
  • 【网络实验】用华为eNSP配置路由器DHCP服务,实现PC自动获取IP地址
  • 如何用10分钟语音数据训练专属AI音色:Retrieval-based-Voice-Conversion-WebUI完整指南
  • 屏幕卡死无法点击?只用键盘重启电脑
  • (毕业必看)实测好用的AI写作辅助软件,毕业党收藏备用
  • 《置身钉内》原文-可播放阅读
  • 打破监控协议壁垒:go2rtc如何让传统摄像头在现代浏览器中焕发新生
  • OpenDroneMap:开源无人机摄影测量系统的架构解析与技术实现
  • 终极指南:Ucupaint让Blender纹理图层管理变得如此简单![特殊字符]
  • PN7642 NFC开发板实战:从硬件连接到射频测试全流程指南
  • 2026年 HC600/980QP高强钢厂家推荐榜单:汽车轻量化核心板材与冲压性能深度解析 - 品牌发掘
  • 原神FPS解锁工具:终极免费突破60帧限制完整指南
  • 嵌入式低功耗实战:从Cortex-M0+睡眠模式到KM35Z75 VLLS3微安级功耗实现