图1 Java虚拟机运行时数据区
做Java就是和内存打交道,如果在这条道路上有更加深入的发展,就必须了解JVM的结构和生命周期。如图所示,图中是一个完整的JVM结构。下面,会列出JVM各个区块的分工。
1. Java栈
Java栈(JVM Stack),通常我们把内存分为堆和栈,这是一种比较粗糙的划分方法,但确实,我们最关心的也就是堆和栈两个主要的内存区块,实际上JVM中的内存分布远比它复杂。
栈,再Java中用来保存8个基本类型数据,也就是局部变量表,以及对象的引用(reference类型,它并不直接代表对象本身。根据不同的JVM虚拟机实现,它可能是一个指向对象起始的内存地址、也可能是代表一个对象的句柄、或者指向一条字节码指令地址)。
其中只有double 和 long 类型(它们是64位的数据长度)数据会占据两个局部变量位置,其他的均占一个局部变量位置,并且局部变量所需的内存空间在编译期就已经确定了。直到方法结束局部变量表的大小都不会改变,所以虚拟机会在进入方法的一开始就直到需要在栈帧中分配多大的内存空间给局部变量表。
上面提到了栈帧,这里说明一下,栈的生命周期和线程的生命周期相同。虚拟机栈所表述的是Java方法的内存模型。每个方法被执行的时候都会在栈中创建一个栈帧来保存方法中的局部变量表、操作栈、动态链接、方法出口等信息。所以每一个方法被执行到方法的结束, 都对应着一个栈帧在Java栈中压栈出栈的过程。
2. 本地方法栈
本地方法栈(Native Method Stack), 本地方法栈, 顾名思义,它是用来调用本地方法的。 它的机制与Java栈(JVM Stack)非常类似。所以有些虚拟机(sun HotSpot)甚至直接将它们两个合起来用。
3. 程序计数器
这是一块很小的内存区域,主要作用是记录当前线程所执行的字节码的行号。字节码解释器工作时就是通过改变当前线程的程序计数器选取下一条字节码指令来工作的。任何分支,循环,方法调用,判断,异常处理,线程等待以及恢复线程,递归等等都是通过这个计数器来完成的。
由于Java多线程是通过交替线程轮流切换并分配处理器时间的方式来实现的,在任何一个确定的时间里,在处理器的一个内核(现在都是多核处理器啊!)只会执行一条线程中的指令。因此为了线程等待结束需要恢复到正确的位置执行,每条线程都会有一个独立的程序计数器来记录当前指令的行号。计数器之间相互独立互不影响,我们称这块内存为“线程私有”的内存。
还有一点要提一下,如果在执行一个方法的时候调用另一个Java方法,那么这个计数器指向的是Java方法字节码中的指令行号,如果是一个native方法,那么这个计数器的值为Undefined。
4. Java堆
Java的堆是JVM内存中最大的一块,它的主要的作用就是用来存放对象,并且这块区域是所有线程共享的。所有的对象,数组都必须在堆中创建(除了现在个别特例JIT实现 【JIT就是用来把java字节码变成可以直接给CPU处理器执行的指令的即时编译器】 可以支持栈上分配)。 同时GC也发生在堆中。由于现在的GC回收策略基本上都是使用代分回收算法, 所以堆中也同时存在多个区域, 例如Eden、Form Survivor、To Survivor、Old Space 、premanent Space等这些都是下一部分的内容,现在可以粗略的了解一下。
值得一提的是,Java堆中的数据在物理上市不连续的,只要逻辑连续即可。但这造成了一个问题, 比如内存中本来数据是相对连续的,有些对象已经不再使用,刚好被GC清理。这时,内存中就会出现如断牙的梳子一般,如果这时你又创建了一个较大对象时(例如2维,3维长数组),虽然剩余的总堆空间确实足够分配如此大的一个对象,却因为不连续性(类似磁盘碎片)而无法在堆中分配出足够大的内存空间因此报出 OutOfMemoryError。幸运的是,GC还有一种清理方式会把当前堆中的内存进行排序。两种方式交替作用下,可以有效的避免这类问题。(关于GC的两种清理方式,参阅Thinking in Java)
5. 方法区
方法区(Method Area)与JAVA堆类似,但是其中存放的是常量,静态方法,类信息,静态常量以及上述提到的JIT(即时编译器)编译后的代码等数据信息的。一般情况下,方法区的内存空间是相对固定的, 除非在初始化的时候由于内存过小而导致无法满足内存分配需求的时候会报出 OutOfMemoryError。
其中需要主要说明的一点是,运行时常量也保存在方法区中,如内设的int值,String常量池等等内容。
JVM生命周期概述
由于我还是个鸟,所以堆JVM生命周期的理解并不是那么详细和到位,所以,下文中的内容如有异议及补充请留言回复。
JVM运行时首先会执行ClassLoader的最上层的BootStrap ClassLoader加载系统类所必须的类(如String,int 等等),由于BootStrap是由C语言编写的所以我们无法再Java中得到它。加载完毕后执行程序main方法,在这个个过程中,会首先将系统所必须的类信息加载入方法区中,并定义String缓冲池int内设值以及涉及到的常量和静态方法。
当遇到需要加载的类时,JVM会再次执行ClassLoader,由于ClassLoader是一个Parent结构,ClassLoader会从最下层的
System ClassLoader(通常我们使用的Class.forName(...)等)向上递归搜索至Extension ClassLoader, 同理Extension ClassLoader向上搜索到 BootStrap ClassLoader , BootStrap会检索自己的加载列表中有没有这个类,如果有则返回这个类的信息,如果没有则会在自己的加载范围内搜索该类并把它加载成Java字节码放入对应的位置,这时,如果是一个用户自定义的类时,由于BootStrap的加载范围不包括用户自定义类,所以结束上层递归由Extension ClassLoader检索自己加载链表,如果没有则同理向下结束中层递归由System ClassLoader来加载, 加载过程同理为先检索后在当前项目ClassPath中查找要加载的自定义类(ClassLoader的机制可以自行去了解 如:http://dlevin.iteye.com/blog/772604)。 当类加载完毕后,JVM会将这个类信息保存到方法区内,并将对应的常量、静态常量方法等信息保存进方法区中。
当程序执行到需要new出一个对象的时候, JVM首先会在栈中创建一个该对象类型的引用, 而后在堆中创建出类的一个对象, 最后将这个对象和栈中对象的引用关联起来, 这里可能是一个内存地址,也可以使一个句柄。 而后,对象执行方法的过程中,栈顶压入一个栈帧,同时程序计数器会记录当前线程中执行的指令行数,并做对应的跳转(如果是内联方法可能不需要跳转)。最后,方法结束,栈帧出栈,并清理栈中的局部变量表以及对象的引用等信息。但对于堆并不急于清理, 因为在编译时编译器并不知道堆中的对象的生命周期。
程序运行了一段时间,堆中的数据将要满时(当需要创建一个对象却发现剩下的内存空间并不能分配足够大的空间时)Java垃圾清理系统(GC)一定会执行,清理掉没有被任何地方引用的对象,释放堆中的内存空间。 这里会出现类似于磁盘碎片的现象(所以我们在写代码的时候尽量不要创建庞大的对象,而是将他作为静态方法放在基本不变的方法区中),一种坏的情况是,随着程序的运行, 不可被回收或者内存不连续序列越来越紧密,最终会导致OutOfMemoryError。不过如果代码优化的够好,直到程序结束,这个现象都不会发生。
当程序结束后,JVM会一次性释放全部当前Java进程中全部内存空间。整个JVM生命周期在此结束了。
-------------------------------
作为补充内容,下次的内容为上述的代分垃圾回收算法介绍。
分享到:
相关推荐
2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...
描述了JVM的工作原理及相应的结构分析,这样可以方便我们对JAVA技术的更深层次的认识
JVM 是 Java 程序的运行环境,学习 JVM,方能了解 Java 程序是如何被执行的,为进一步深入底层原理乃至程序性能调优打好基础。通过学习这门课程,你将掌握:1. JVM 内存结构的组成、各部分功能作用,学会利用内存...
主要介绍了深入理解JVM之Class类文件结构,结合实例形式详细分析了Class类文件结构相关概念、原理、结构、常用方法与属性,需要的朋友可以参考下
第58节死锁原理以及可视化虚拟机工具-Jconsole线程死锁监控00:10:38分钟 | 第59节VisualVM使用详解00:08:03分钟 | 第60节性能调优概述00:11:22分钟 | 第61节性能调优-案例100:23:28分钟 | 第62节性能调优-案例...
学习资料包括JVM简介、作用和特征、工作原理、执行原理、原理结构、类加载原理详解等
├─1.01 HTTP协议-工作原理及消息结构.mp4 ├─1.02 HTTP协议-客户端请求信息及服务端响应信息.mp4 ├─1.03 HTTP协议-请求方法及响应头信息.mp4 ├─1.04 HTTP协议-状态码.mp4 ├─1.05 Nginx进阶基础-Nginx介绍及...
我们创建一个类,通过编译,生成对应的.calss文件,之后使用java.exe加载(jvm的类加载器)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在缓存区,那么这个运行时类的本身就是一个class的实例 ...
线程栈及栈帧内部结构详解 程序计数器详解 本地方法栈详解 堆详解 JVM启动参数设置 堆内存大小设置 方法区内存大小设置 栈分配内存大小设置 JVM对象创建原理 分配内存过程 o指针碰撞 o空闲...
JVM特点,结构与执行周期 JVM类加载机制 JVM运行时区数据 JVM执行引擎和垃圾回收 基础语法 理解Java中对象基础Object类 基本数据类型,核心点整理 特殊的String类,以及相关扩展API 日期与时间API详解 流程控制语句...
Java 基础 JAVA 开发基础知识 Eclipse 开发环境 多线程技术 Socket 网络技术 Regular Expression Java 反射技术 各种实战设计模式 面向对象设计原则详解 Java collection Reflection JVM 原理与配置、调优 Java 设计...
了解Class文件的结构组成,对于我们后续的JVM以及Java原理深入学习是很有帮助的,因为Class文件帮我们默默的做了很多事,比如、为什么对象方法中可以直接使用this变量?!本文将带领大家,一步步,从开头到结尾,...
SQL执行原理详解 索引底层剖析 执行计划与SQL优化 Mysql锁机制与事务隔离级别详解 并发编程 JMM内存模型 并发同步处理 并发包之tools工具 并发包之atomic原子操作 阻塞队列BlockingQueue详解 并发Map、Lis
HBase、 Java9 、Java10 、MySQL优化 、JVM原理 、JUC多线程、 CDH版Hadoop Impala、 Flume 、Sqoop、 Azkaban、 Oozie、 HUE、 Kettle、 Kylin 、Spark 、Mllib机器学习、 Flink、 Python、 SpringBoot、 Hadoop3.x...
不同垃圾收集器工作原理详解 GC日志分析 Spring全家桶源码分析 Tomcat架构原理 Web请求处理原理 数据访问层框架原理 架构与设计思维模式 程序中的数学 数据分析 机器智能算法剖析与应用 云原生 自动化DevOps 流量...
Java-Base64算法_创新_防止表单重复提交 ...数据库连接池原理详解 Java企业级框架之核心技术(反射) Java-Base64算法(创新_防止表单重复提交) 揭开springAOP神秘面纱之动态代理 网络爬虫之JAVA正则表达式
Android+JAVA【面试题】精编汇总(25份): 2019Android面试 常见58...腾讯Android社招面试源码相关11题+原理详解.docx 设计模式面试专题.docx 设计模式面试专题及答案.pdf 面试必备之乐观锁与悲观锁.pdf 高级面试题.docx
收集整理的Android【面试】资料精编汇总(25个) ...腾讯Android社招面试源码相关11题+原理详解.docx 设计模式面试专题.docx 设计模式面试专题及答案.pdf 面试必备之乐观锁与悲观锁.pdf 高级面试题.docx
总结整理的Android面试Java基础知识点面试资料精编汇总文档资料...腾讯Android社招面试源码相关11题+原理详解.docx 设计模式面试专题.docx 设计模式面试专题及答案.pdf 面试必备之乐观锁与悲观锁.pdf 高级面试题.docx
类加载机制详解 垃圾回收机制 垃圾回收器、垃圾回收算法 ARM与多线程 多线程基础知识 常见关键字 多线程锁机制 线程池知识点 常见的JUC工具类 多线程经典面试题 常用工具集 JVM问题排查工具-JMC IDEA开发神器 线上...