深入理解JVM

概述JVM

JVM简介:

JVM全称是Java VirtualMachine,Java虚拟机,也就是在计算机上再虚拟一个计算机,这和我们使用 VMWare不一样,那个虚拟的东西你是可以看到的,这个JVM你是看不到的,它存在内存中。我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个JVM也是有这成套的元素,运算器是当然是交给硬件CPU还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM自己的命令集,这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的CPU,比如8086系列的汇编也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集则是可以到处运行的,因为JVM做了翻译,根据不同的CPU,翻译成不同的机器语言。

JVM中我们最需要深入理解的就是它的存储部分,存储?硬盘?NO,NO,JVM是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效,接下来的部分就是重点介绍之。

JVM组成

从这个图中可以看到,JVM是运行在操作系统之上的,它与硬件没有直接的交互

我们再来看下JVM有哪些组成部分,如下图所示:

Runtime Data Area 运行时数据区

Stack 栈

Heap 堆

Mathod Area 方法区

PC Register 寄存器

Native Method Stack 本地方法栈

Execution Engine 执行引擎

Native Interface 本地接口

Native Libraies 本地包集合

Class Loader类加载器

类加载器的作用是加载类文件到内存,比如编写一个HelloWord.java程序,然后通过javac编译成class文件,那怎么才能加载到内存中被执行呢?Class Loader承担的就是这个责任,那不可能随便建立一个.class文件就能被加载的,Class Loader加载的class文件是有格式要求,在《JVM Specification》中式这样定义Class文件的结构:

ClassFile{

u4magic;

u2minor_version;

u2major_version;

u2constant_pool_count;

cp_infoconstant_pool[constant_pool_count-1];

u2access_flags;

u2this_class;

u2super_class;

u2interfaces_count;

u2interfaces[interfaces_count];

u2fields_count;

field_infofields[fields_count];

u2methods_count;

method_infomethods[methods_count];

u2attributes_count;

attribute_infoattributes[attributes_count];

}

需要详细了解的话,可以仔细阅读《JVM Specification》的第四章“The class File Format”,这里不再详细说明。

Execution Engine执行引擎

执行引擎也叫做解释器(Interpreter),负责解释命令,提交操作系统执行。

Native Interface本地接口

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍。

Runtime data area运行数据区

运行数据区是整个JVM的重点。我们所有写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治,下一章节详细介绍之。

整个JVM框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,瞧,一个完整的系统诞生了!

Java运行数据区

PC程序计数器:是一块较小的内存空间(其实就是指针),它的作用可以看作是当前线程所执行的字节码的行号指示器。每一个线程都有自己私有的程序计数器。如果线程正在执行的是一个JAVA方法,该计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是native方法,则计数器值为空(undefined)。此内存区域是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

JAVA虚拟机栈:也是线程私有的,生命周期和线程相同。每个方法被执行的时候都会同时创建一个栈帧(stack frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中从入栈道出栈的过程。

JAVA堆:JAVA堆是被所有线程共享的一块内存区域,在JVM启动时创建。其作用就是存放对象实例。

JVM规范规定:所有的对象实例及数组都要在堆上分配。

JAVA堆也是垃圾回收管理的主要区域。

由于现在收集器基本采用分代收集算法,所以JAVA堆还可以细分为:新生代和老年代,再细分还有Eden空间、From Survivor空间、To Survivor空间。

根据JVM规范,JAVA堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,可以是固定大小的,也可以是可扩展的(用-Xmx和-Xms控制),如果堆中没有内存,也无法扩展,抛出OutOfMemoryError

方法区:也是所有线程共享的内存区域。它用于存放虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

在JVM规范中被描述为堆的一部分,但却又有一个non-heap的别名。

对于HOTSPOT虚拟机,方法区又被称为永久代(Permanent Generation)

这个区域一样不需要连续的内存,可以选择固定大小或者扩展,还可以选择不实现垃圾回收。确实这个区域的数据一般不参与回收,但这些数据并不一定就是永久存在了,常量池和对类型的卸载也可以成为回收的目标。抛出OutOfMemoryError

运行时常量池:是方法区的一部分。

在编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

Class加载过程

类加载按加载,连接,初始化这个顺序进行的,其中连接又可以细分为验证,准备,解析三个阶段,部分解析可以在初始化开始之后再开始,这样可以支持java的运行时绑定。虽然部分解析可以在初始化阶段开始以后再开始,但是这部分的初始化还是需要当前的部分解析以后才可以初始化。java虚拟机规范中严格规定了有且之友中情况必须立即对类进行初始化:

例:

public class TestLoad{

final static String aaa="aaaaaaa";

int a=add(0);

public static void main(String[] args) {

// TODO Auto-generated method stub

TestLoad test=new TestLoad ();

try {

Class bean = Class.forName("synchronize.TestLoad ");

Field f[] =bean.getDeclaredFields();

for(int i=0;i<f.length;i++)

{

System.out.println("f"+i+":"+f[i].getName());

}

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("a初始值:"+test.a);

int b=test.add(test.a);

System.out.println(b);

}

int add(int a){

a=a+10;

return a;

}}

下面让我们以TestLoad Class加载过程为例:

1、 加载

首先ClassLoad加载器加载Class文件。主要在方法区中保存了如下信息

① 这个类的完整有效名

② 这个类的父类完整有效名

③ 这个类的修饰符

④ 这个类的接口

⑤ 这个类的Method方法信息

⑥ 这个类的Field信息

⑦ 这个类的常量或static

⑧ 等其他信息

方法的图只画出来方法区中的部分信息。

创建一个Class对象,对象都在heap区。

① 首先根据方法区的Field变量创建一个Field类型的对象

② 根据方法区中的方法创建Method类型的对象

③ 根据需要创建需要的SoftReference的对象,SoftReference是一个泛型容器存放Field和Method的对象

④ 创建一个Class对象,里面变量引用③中创建的对象

2、 连接

我只能自己想个大概,说不出来,请会的朋友告诉我

3、 初始化

在加载和连接后,需要对类的全局变量进行初始化。int a=add(0); add方法在main函数开始执行之前就执行了。

运行过程

栈也叫栈内存,是Java程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就Over。问题出来了:栈中存的是那些数据呢?又什么是格式呢?

栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,执行完毕后,先弹出F2栈帧,再弹出F1栈帧,遵循“先进后出”原则。

那栈帧中到底存在着什么数据呢?栈帧中主要保存3类数据:本地变量(LocalVariables),包括输入参数和输出参数以及方法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作;栈帧数据(FrameData),包括类文件、方法等等。

我的理解:Java运行过程,是PC读取方法区的指令,操作栈中的对象的过程。

时间: 2024-10-12 10:15:52

深入理解JVM的相关文章

HashMap工作原理、深入理解JVM、正则

HashMap工作原理: http://www.importnew.com/7099.html: http://blog.csdn.net/ghsau/article/details/16843543: http://blog.csdn.net/ghsau/article/details/16890151. 深入理解JVM: http://www.importnew.com/17770.html: http://www.cnblogs.com/dingyingsi/p/3760447.html.

【转】[译]深入理解JVM

http://www.cnblogs.com/enjiex/p/5079338.html 深入理解JVM 原文链接:http://www.cubrid.org/blog/dev-platform/understanding-jvm-internals 每个使用Java的开发者都知道Java字节码是在JRE中运行(JRE: Java 运行时环境).JVM则是JRE中的核心组成部分,承担分析和执行Java字节码的工作,而Java程序员通常并不需要深入了解JVM运行情况就可以开发出大型应用和类库.尽管

深入理解JVM之JVM内存区域与内存分配

深入理解JVM之JVM内存区域与内存分配 在学习jvm的内存分配的时候,看到的这篇博客,该博客对jvm的内存分配总结的很好,同时也利用jvm的内存模型解释了java程序中有关参数传递的问题. 博客出处: http://www.cnblogs.com/hellocsl/p/3969768.html?utm_source=tuicool&utm_medium=referral 看了此博客后,发现应该去深入学习下jvm的内存模型,就是去认真学习下<深入理解Java虚拟机>,其内容可能会<

【转】理解JVM内存区域

引言 对于C++程序员,内存分配与回收的处理一直是令人头疼的问题.Java由于自身的自动内存管理机制,使得管理内存变得非常轻松,不容易出现内存泄漏,溢出的问题. 不容易不代表不会出现问题,一旦内存泄漏或溢出的情况发生,调试起来会变得非常困难.这就要求我们对虚拟机的内存区域有深入的理解.最终能够判断内存方面的异常发生时,具体在JVM中的位置. 内存区域 JVM运行时,首先需要类加载器(ClassLoader) 加载所需类的字节码,加载完毕交由执行引擎执行,执行过程中需要一段空间来存储数据(类比CP

[译]深入理解JVM

深入理解JVM 原文链接:http://www.cubrid.org/blog/dev-platform/understanding-jvm-internals 每个使用Java的开发者都知道Java字节码是在JRE中运行(JRE: Java 运行时环境).JVM则是JRE中的核心组成部分,承担分析和执行Java字节码的工作,而Java程序员通常并不需要深入了解JVM运行情况就可以开发出大型应用和类库.尽管如此,如果你对JVM有足够了解,就会对Java有更好的掌握,并且能解决一些看起来简单但又尚

【深入理解JVM】:类加载机制

概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 与那些在编译时需要进行链接工作的语言不同,在Java语言里,类型的加载.连接和初始化过程都是在程序运行期间完成的,例如import java.util.*下面包含很多类,但是,在程序运行的时候,虚拟机只会加载哪些我们程序需要的类.这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性. 类加载的时机 类从

深入理解jvm之一【内存区域】

文章开始之前,首先需要申明,本系列文章讨论的是HotSpot VM,文章中多数观点基于<深入理解Java虚拟机:JVM高级特性与最佳时间   周志明>,笔者如有理解错误,欢迎指正. 在开始探索jvm虚拟机之前,不得不对jvm的内存区域进行讨论,依旧先附上图表: 程序计数器 程序计数器,也能叫做PC寄存器,从名字上来理解可能会把它想成一个计数的内存区域,但是,了解汇编的人会知道,程序技术器实际上是CPU上的一个寄存器,它保存当前指令执行的地址(也可以说下一条指令所在的存储单元).当cpu执行该线

深入理解JVM内幕:从基本结构到Java 7新特性

转自:http://www.importnew.com/1486.html 每个Java开发者都知道Java字节码是执行在JRE((Java Runtime Environment Java运行时环境)上的.JRE中最重要的部分是Java虚拟机(JVM),JVM 负责分析和执行Java字节码.Java开发人员并不需要去关心JVM是如何运行的.在没有深入理解JVM的情况下,许多开发者已经开发出了非常多的优秀 的应用以及Java类库.不过,如果你了解JVM的话,你会更加了解Java的,并且你会轻松解

深入理解JVM—性能监控工具

我们知道,在JVM编译期和加载器,甚至运行期已经做了大量的调优操作,但是那些都是JVM针对Java程序所做的通用的.简单的优化,程序在运行时由于运行环境的复杂性.业务逻辑的复杂性,很多JVM是无法进行优化处理的,这就需要我们自己在写代码的时候就注意,以便我们的程序在特定的业务场景发挥到最佳性能. 要进行性能调优,首先我们要找到程序的性能瓶颈在哪里?而要知道性能瓶颈在哪里,我们需要借助一定的工具进行处理. 在windows操作系统下,当我们的系统运行很慢的时候,80%的人首先查看的就是任务管理器,

通俗易懂理解JVM结构

通俗易懂理解JVM结构 说明:本篇内容是结合网上各位大牛的关于JVM的文章,通过作者的理解,希望以一种比较易懂的方式,让各位朋友们理解JVM到底是怎么一回事儿,其中部分图片和内容引用来自于网络,如有雷同,请见谅~~ 一.JVM内存区域模型是啥样? 这个是JVM大致的内存分布模型,看起来比较直观: 这个是更精细化的JVM内存模型,区别主要是方法区和堆是公共内存区,其他是私有的: 1.方法区: 也称"永久代" ."非堆", 它用于存储虚拟机加载的类信息.常量.静态变量.