前言:该篇是参考结合<高手深度解析:JVM是什么>,自己剔除添加了一些内容整理的而来,这里感谢<高手深度解析:JVM是什么>的文章的指点讲解。

JVM的生命周期

“JVM实例和JVM执行引擎实例,JVM实例对应了一个独立运行的Java程序,而JVM执行引擎实例对应了属于用户运行程序的线程;也就是JVM实例是进程级别,而执行引擎是线程级别的”。

JVM实例的诞生:启动一个带有main方法的Java程序时,一个JVM实例就产生了,此时类加载器就会以main方法为切入点来开始程序的执行。

JVM实例的运行:main()作为该程序初始线程的起点,所有的任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,Java程序也可以标明自己创建的线程是守护线程。

JVM实例的消亡:当程序中的所有非守护线程都终止时,JVM才退出

JVM的体系结构

JVM的内部体系结构分为三部分:类装载器子系统,运行时数据区和执行引擎。

1、类装载器,所有的类的加载都是由类加载器来完成的,并且Java中的类加载器是动态加载的,只有使用的时候才会主动去加载。

当执行 java MyFirstClass命令时候,它的简单的类加载过程如下:

1) 寻找jre目录,找到jvm.dll,根据它来初始化一个JVM;

2) 产生一个Bootstrap Loader(启动类加载器);

3) Bootstrap Loader自动加载Extended Loader(标准扩展类加载器),并将其父Loader设为Bootstrap Loader。

4) Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。

5)最后由AppClass Loader加载MyFirstClass类。

2、执行引擎:它主要是执行字节码或者本地方法

执行引擎定义了一个指令集,每一条指令包含一个单字节的操作码,后面跟0个或者多个操作数。这些就是主要用来执行经过编译后的字节码指令。

3、运行时数据区:方法区,堆,虚拟机栈,PC寄存器,本地方法栈

(1)方法区和堆由所有线程共享

堆:存放所有程序在运行时创建的对象

方法区:当JVM的类装载器加载.class文件,并进行解析,把解析的类型信息放入方法区。

(2)Java栈和PC寄存器和本地方法栈由线程独享,在新线程创建时间里

JVM中的数据类型:包括基本类型和引用类型,

(1)基本类型:8种基本数值类型和returnAddress(JVM的内部类型,用来实现finally子句)。

(2)引用类型:数组类型,类类型,接口类型。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。在Java中,Main函数就是栈的起始点,也是程序的起始点。

程序计数器:

程序计数器,一般占据一块较小的空间,主要就是作为当前线程执行字节码时候当前行号的指示器。字节码解释器就是通过改变计数器值来执行下一条字节码指令。之所以是由线程私有的,是因为CPU会轮流的交替执行各个线程的任务,而为了使得切换后的线程能正确恢复到执行位置,此时就需要为每个线程记录每次执行字节码的记录数。一般执行JAVA方法,该计数器记录是字节码指令的地址,而如果执行native方法,这个计数器值为空。这个区域是唯一一个不会出OutOfMemoryError的区域。

虚拟机栈:

虚拟机栈即Java栈,它也是线程私有的,该栈的生命周期同线程一起生一起亡,它所描述的就是Java的方法在执行中的内存模型:每个线程在执行方法的时候都会创建一个栈帧,它主要用于存储局部变量表,操作数栈,动态链接和方法出口等信息。该栈帧有两种操作,即帧的压栈和出栈。

每个栈帧代表一个方法,Java方法有两种返回方式,return和抛出异常,两种方式都会导致该方法对应的帧出栈和释放内存。

对于局部变量表存放编译期可知的8种基本数据类型,对象引用类型,和returnAddress类型,它所需要的内存大小在编译期间都完成分配了。

本地方法栈:

本地方法栈是为虚拟机在使用native方法的时候提供服务,而虚拟机站是为虚拟机执行java方法(字节码)提供服务。本地方法栈主要依赖于本地方法的实现,如某个JVM实现的本地方法接口使用C连接模型,则本地方法栈就是C栈,可以说某线程在调用本地方法时,就进入了一个不受JVM限制的领域,也就是JVM可以利用本地方法来动态扩展本身。

在HotSpot虚拟机上,本地方法栈和虚拟机栈合二为一。

堆:

在Java中“一切皆对象”,而存储这些Java对象的实例变量就是在堆中,所有的对象实例以及数组都在堆上分配。该堆中存放的数据主要有对象实例变量(包括自己所属的类和其父类声明的)以及指向方法区中对象类型数据的指针,指向方法表的指针。

在方法中利用Object obj=new Object();

Object obj会反映到Java栈的本地变量表中,它作为一个reference类型存储的。new
Object()则是反映到Java堆中,直接在堆中分配,凡是利用new实例化对象,都是在堆中分配内存的。一个在Java栈中,一个在Java堆中,该reference类型所存储的信息就是为了要关联堆中实例对象的地址与栈中的变量地址。reference类型它只是用来作为一个指向对象的引用,它直接指向了该Java堆中对象实例数据的地址,该地址中包含了该对象类型数据的指针。之所以需要对象类型数据的指针的时候,是因为在java中对象转换过程中需要判断对象类型,以及java利用多态进行处理的时候,动态绑定在运行的过程中需要知道具体的对象类型是什么。

一般将堆划分为年轻代和老年代。

方法区:

在我们编程中,利用getClass来获取类型信息,而这个类型信息就是存放在方法区的,所以方法区存储了类的基本信息即类型信息,常量,静态变量,即使编译器编译后的代码。方法区在HotSpot中也叫永久代。那么类型信息在Java中是利用大端序的(即低字节的数据存储在高位内存上)。

类型信息:包括class的全限定名,class的直接父类,类类型还是接口类型,类的修饰符,所有直接父接口的列表,Class对象提供了访问这些信息的窗口

指向ClassLoader类的引用:在动态连接时装载该类中引用的其他类

指向Class类的引用:

在方法区中,有一块是称为运行时常量池,主要用来存放编译期生成的各种字面量和符号引用。

常量池:包括直接常量(String,integer和floatpoint常量)以及对其他类型、字段和方法的符号引用,由于这些符号引用,使得常量池成为Java程序动态连接中至关重要的部分

字段信息:普通意义上的类型中声明的字段

方法信息:类型中各个方法的信息

编译期常量:指用final声明或者用编译时已知的值初始化的类变量

class将所有的常量复制至其常量池或者其字节码流中。

方法表:一个数组,包括所有它的实例可能调用的实例方法的直接引用(包括从父类中继承来的)

除此之外,若某个类不是抽象和本地的,还要保存方法的字节码,操作数栈和该方法的栈帧,异常表。

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?

第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。

第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。

第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。

第四,面向对象就是堆和栈的完美结合。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。

后记:

从大的方面来说,可以将JVM管理内存分为两种类型:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给
自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。

在设置JVM参数的过程中要注意,-Xms -Xmx这两个指的就是年轻代与老年代的大小之和的堆大小,指的也就是我们逻辑上所说的堆的大小是多大,这里-Xmx指的是堆的大小,但是实际上它仅仅指的是年轻代和老年代总和的大小,-Xmn指的是年轻代大小,则利用Xmx-Xmn就可以得出老年代的大小了。而-XX:PermSize
-XX:MaxPermSize这两个指的就是持久代的大小,严格意义上-Xmx和-XX:MaxPermSize才是JVM中堆的完整大小,-Xss是主要指的线程栈的大小,当在多线程中注意分配该大小。

JVM内存主要由5大部分组成,JVM的完整大小,可以通过以下参数设置,这里将年轻代与老年大的大小称为堆大小

-Xms(年轻代与老年代的初始大小最小大小) 如-Xms20M

-Xmx(堆的最大值)

-Xmn(堆中年轻代的大小)

-XX:SurvivorRatio(堆中年轻代的Eden区与一个Survivor的区大小比例) 如-XX:SurvivorRatio=8

-XX:MaxPermSize(持久代的大小)

-Xss(线程栈的大小)

这几个参数来完整的布局好。这里设定了年轻代的大小,老年大的大小,年轻代中Eden与一个Survivor区域的比例大小,持久代的大小,以及栈的大小,这样整个JVM的内存布局分配完毕了。

本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1220075

我和JVM初次约会的更多相关文章

  1. Things about single men that women hate

    Things about single men that women hate为何你俘获不了女神的心?If you listen in to a group of single women talki ...

  2. 【JVM】JVM系列之内存模型(六)

    一.前言 经过前面的学习,我们终于进入了虚拟机最后一部分的学习,内存模型.理解内存模型对我们理解虚拟机.正确使用多线程编程提供很大帮助.下面开始正式学习. 二.Java并发基础 在并发编程中存在两个关 ...

  3. java jvm常用命令工具

    [尊重原创文章出自:http://www.chepoo.com/java-jvm-command-tools.html] 一.概述 程序运行中经常会遇到各种问题,定位问题时通常需要综合各种信息,如系统 ...

  4. 意译:《JVM Internals》

    译者语 为加深对JVM的了解和日后查阅时更方便,于是对原文进行翻译.内容是建立在我对JVM的认识的基础上翻译的,加上本人的英语水平有限,若有纰漏请大家指正,谢谢. 原文地址:http://blog.j ...

  5. JVM知识点

    先发个链接到两位大牛的主页 http://rednaxelafx.iteye.com/               http://icyfenix.iteye.com/ 目录 1)概述 2)编译 3) ...

  6. jvm(13)-线程安全与锁优化(转)

    0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...

  7. JVM面试

    深入理解Java内存模型:http://www.cnblogs.com/skywang12345/p/3447546.html http://www.infoq.com/cn/articles/jav ...

  8. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  9. 208道面试题(JVM部分暂无答案)

    这是从网上看到的一套java面试题, 答案只是一个大概, 另外题目质量参差不齐, 斟酌参考(JVM的部分暂时没有答案) 一.Java 基础 JDK 和 JRE 有什么区别? 答: JDK(Java D ...

  10. JVM规范系列第5章:加载、链接与初始化

    加载是根据特定名称查找类或接口类型的二进制表示(Binary Representation),并由此二进制表示创建类或接口的过程. 加载,就是指去寻找类或接口的过程. 链接是为了让类或接口可以被 Ja ...

随机推荐

  1. 编译器实现之旅——第十六章 代码装载、链接器、全局变量与main函数

    在上一章的旅程中,我们已经实现了函数调用的代码生成器分派函数,但在上一章的末尾,我们留下了三个问题: 我们需要为全局变量压栈 main函数需要在程序启动时被自动调用 我们需要实现一个链接器,以将所有的 ...

  2. 图文教程:从0到1将项目发布到 Maven 中央仓库

    前言 本文基于官方文档 https://central.sonatype.org/publish/publish-guide/ 编写. 发布步骤: 创建账号 创建用户 Token 创建命名空间 配置 ...

  3. Spark 知识

    Spark 学习 Spark(一): 基本架构及原理 https://www.cnblogs.com/xing901022/p/8260362.html https://www.cnblogs.com ...

  4. HTML – Native Dialog Modal

    前言 之前介绍 Native Form 的时候有提及过 method="dialog", 但由于它太新了, 所以没去研究. 这篇就介绍一下. Dialog 也好 Modal 也好, ...

  5. SpringMVC —— 日期类型参数传递

    日期类型参数传递    相关注解    类型转换器   

  6. C# 开源浏览器性能提升,体验Chrome级速度

    前言 使用 C# 和 CefSharp 开发的全功能网页浏览器. 项目介绍 SharpBrowser 是目前最快的开源 C# 网页浏览器! 采用了轻量级的 CEF 渲染器,在呈现网页时甚至比 Goog ...

  7. Android Qcom USB Driver学习(七)

    最近遇到了USB 插拔后,系统重启的问题,抓取串口log发现如下问题,log中查看trace分析就是空指针造成的panic Unable to handle kernel read from unre ...

  8. 关于Transformer中feed forward layer理解

    今天记录一下个人关于Transformer中前馈神经网络(FFN)的一点理解. 第一点,我们知道,FFN之前是有一个多头注意力机制的模块.我们可以想想一下,如果这个模块直接输出,不经过FFN层会发生什 ...

  9. 2024csp初赛总结

    浙江27日下午1:30出分了,j组97,s组61.5,和估分一模一样,还好没有挂分.然后3点的时候上洛谷看了一下,全国分数线出了,j组89分,s组56分.那应该都过了,随后同学的成绩也出来了,sjx, ...

  10. 使用 KubeKey 在 AWS 高可用部署 Kubernetes

    作者:李耀宗 介绍 对于生产环境,我们需要考虑 Kubernetes 集群的高可用性.本文教您部署如何在多台 AWS EC2 实例快速部署一套高可用的生产环境.要满足 Kubernetes 集群服务需 ...