浅谈JVM及原理

1、什么是JVM ?

JVM, 中文名是Java虚拟机, 正如它的名字, 是一个虚拟机器,来模拟通用的物理机。 JVM是一个标准,一套规范,  规定了.class文件在其内部运行的相关标准和规范。 及其相关的内部构成。 比如:所有的JVM都是基于栈结构的运行方式。那么不符合这种要求的,不算是JVM, 如Android中所使用的Dalvik 虚拟机就不能称作是JAVA 虚拟机, 因为它是基于寄存器(最新的Android系统据说已经放弃了Dalvik
VM, 而是使用ART)。

JVM相关的产品有很多, 通常最有名的莫过于现在Oracle公司所有的HotSpot 虚拟机。因此, 这里讨论的都是HotSpot虚拟机, 如果没有特别说明。

2、类加载?

类加载, 是通过JVM的类加载器从JVM外部以二进制字节流的方式加载到JVM中。但JVM本身有至少三种类加载器:BootStrap(根类加载器,C++实现, 加载位于jre/lib/rt.jar)、Extension(扩展类加载器, 主要用于加载jre/lib/ext/下的jar)、System(加载classpath环境变量所指定的class);当然还有,自定义的类加载器(用于实现自己的类加载器,
如Tomcat中就实现多个类加载器,用来管理不同的jar)。

如果, 我有一个HelloWorld的类需要加载, 首先类加载器会去从最底层的类加载器去验证这个类是否被加载, 如果没有, 则委托给上一次的类加载器验证是否被加载, 如果到BootStrap类加载器都没有发现HelloWorld类被加载, 那么类加载器将执行加载任务, 如果根类加载器没有加载, 则委托给下一级的Extension类加载器去尝试加载,直到这个类被加载成功。
参考下图:

需要注意的是:如果一个类被不同的类加载器加载, 那么就是两个不同的类。

3、类加载的具体过程?

被java编译器(不仅限于, 还有其他任何的可以编辑成为.class的编译器)编译过的.class文件(可能是以jar、war、jsp等形式), 经过类加载器加载 、 验证、准备、解析、初始化之后, 才可以被使用。基本的过程如下:

  • 加载: 首先,通过一个类的全类名来获取此类的二进制字节流。其次,将类中所代表的静态存储结构转换为运行时数据结构, 最后,生成一个代表加载的类的java.lang.Class对象, 作为方法区这个类的所有数据的访问入口。加载完成之后, 虚拟机外部的二进制静态数据结构就转换成了虚拟机所需要的结构存储在方法区中(至于如何转换, 则由具体虚拟机自己定义实现), 而所生成的Class对象,
    则存放在方法区中, 用来作为程序访问方法区中数据的外部接口。
  • 验证:其目的就是保证加载进来的.class文件不会危害到虚拟机本身, 且内容符合当前虚拟机规范要求。主要验证的内容大致有:文件格式、元数据验证、字节码验证、符号引用验证。其中文件格式验证, 主要确保符合class文件格式规范(如文本后缀为.class的文件将验证不通过), 以及主次版本号, 验证是否当前JVM可以处理等。元数据验证,主要验证编译后的字节码描述信息是否符合java语法规范。字节码验证,
    其最为复杂, 主要通过控制流和数据流确定语义是否合法、符合逻辑。符号引用验证,可以看做是除自身以外(常量池中各种引用符号)的信息匹配校验,如通过持有的引用能否找到对应的实例。
  • 准备:正式为类变量分配内存,并设置类变量的初始值。这些变量都会在方法区中进行分配。
  • 解析:将常量池内的符号引用替换为直接引用的过程。主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄等。
  • 初始化:加载的最后阶段, 程序真正运行的开始。

4、java运行时数据区 ?

既然类以及加载到JVM中, 那么数据如何真正的运行?如下图:

类加载进来, JVM是通过上图所示的区域来运行和管理这些加载进来的CLASS。即程序运行的是时候, 由上面逻辑单元来运行程序, 包括:方法区、堆、本地方法栈、栈、程序计数器(PC)五大部分组成(有些VM说常量池也是其中的一个单元, 但是HotSpot VM中的常量池是方法区中的一部分)。(注意线程共享)

  • 程序计数器 (PC):可以看做是当前线程执行字节码的行号指示器。字节码解释器工作的时候就是通过这个计数器的值来选取下一条需要执行的字节码指令, 分支, 循环、跳转、异常处理、线程恢复等基础功能依赖计数器完成。
  • 虚拟机栈:和计数器一样, 也是线程私有的,生命周期同线程一致。每个方法在执行时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。方法调入则入栈, 方法执行完则出站。局部变量表存储各种基本类型数据(java的8种,其中long,double占用2个局部变量控件,其余数据占用1个)、对象引用(reference类型)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时, 这个方法需要在帧中分配多大的局部变量空间是完全确定的。在方法运行期间是不会改变局部变量表的大小的。
  • 本地方法栈:此栈和JVM栈作用非常类似, 不同在于本地方法栈为虚拟机使用到的Native方法服务, 而JVM栈则是为Java执行的方法服务。Sun HotSpot虚拟机, 直接把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverFlowError和OutOfMemoryError异常。
  • Java堆:是JVM管理内存中最大的一块。被所有线程共享一块区域。堆是GC垃圾收集器管理的主要区域。从内存回收角度看, java堆被分为新生代、老年代, 再细致一点有其他的划分。这些目的主要就是更快的分配和回收内存。
  • 方法区:和java堆相同, 线程共享区域, 用来存储已被虚拟机加载的类信息, 常量、静态变量、即时编译器编译后的代码等数据。有人称作此方法区为“永久带”, 本质上不等价,只是HotSpot VM将GC分代收集扩展到了方法区,这样HotSpot的垃圾收集器管理方法区和管理java堆一样(优点:不用专门为方法区写一套垃圾收集器, 缺点:容易导致内存溢出)。官方现在拥也有放弃永久带并改为采用Native Memory来实现方法区的计划,目前已经发布的JDK7中的HotSpot中, 已经将原本放在方法区中的字符串常量池移出了。
  • 运行时常量池:是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述外,还有一项就是常量池, 用于存放编译期间生成的各种字面量和符号引用 ,这部分内容在类加载后进入方法区的运行时常量池中存放。

5、垃圾收集?

在java运行时区域中, 程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随程而灭。因为这几个区域

的内存分配和回收都是具有确定性,这几个区域不需要过多考虑回收的问题。因为方法结束之后或线程结束之后,

内存自然就跟着回收了(这不是绝对的, 因为如果当栈内存中的引用很消耗内存的时候, 需要手动将引用置为null,

以便垃圾收集器回收大对象)。而java堆和方法区不一样,一个接口中的多个实现类需要的内存可能不一样, 一个

方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时,才知道会创建哪些对象, 垃圾收集

器关注的就是这部分内存。其也是动态的。

垃圾收集器的区域如下图:

垃圾收集本是有一套非常复杂的算法, 如果在方法区中(HotSpot VM中的永久带)进行垃圾收集, 那么其性价比极

底的,因为垃圾回收主要收集永久带中的两部分内容:废弃的常量和无用的类。回收永久带中的常量和方法区非常相

似。但是在堆中, 尤其是在新生代中,常规应用进行一次垃圾收集, 一般可以回收70%——95%的空间。而永久带

的垃圾收集要远地与此。

如上图所示, 每一个黑框中都是一个垃圾收集器, 对应特定的垃圾收集算法, 来挺高整体的垃圾收集效率。

				
时间: 2024-11-05 04:53:15

浅谈JVM及原理的相关文章

[转] 浅谈 ArrayList 内部原理

转自:浅谈 ArrayList 内部原理 System.Collections.ArrayList 就是我们常说的动态数组,也是我们常用的 "数据类型" 之一.在 MSDN 上是这样表述的:使用大小可按需动态增加的数组实现 IList 接口.我来解释一下,就是:一个可以根据需要动态增加使用大小并可按照索引单独访问的对象的非泛型集合.一般人都认为 ArrayList 就是一个 "纯动态" 的数组,与 <数据结构> 中 "链表" 的原理一

浅谈webpack打包原理

浅谈webpack打包原理 模块化机制 webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目.有了webpack,你可以随意选择你喜欢的模块化方案,至于怎么处理模块之间的依赖关系及如何按需打包,webpack会帮你处理好的. 关于模块化的一些内容,可以看看我之前的文章:js的模块化进程 核心思想: 一切皆模块: 正如js文件可以是一个"模块(module)"一样,其他的(如css.image或html)文件也可视作模 块.因此,你可以require('

&nbsp; &nbsp; &nbsp;【原创】浅谈安全狗原理,不启动安全狗使用防火墙 &nbsp;

最近两天在服务器装了安全狗,由于服务器并没有注册系统服务,所以安全狗一直不起作用 后来注册了apache2a. 把安全狗装好了,然后测试了一***入 说明成功了.但是重启之后apache服务又没了,于是我就纠结,看了一下apache的配置文件: 发现 底部多了如下三行代码: #Begin SafeDogSite-ApacheFilter edits - remove only on uninstall Include "f:/jspstudy/apache/conf/SafeDogSiteApa

浅谈html运行原理

浅谈HTML运行原理,所谓的HTML简单的来说就是一个网页,虽然第一节就讲html原理可能大家会听不懂,就当是给一个初步印象把,至少大概知道一个网页的运行流程是怎样的,下面上一张图: 大致的一个html的运行原理就是如图所示,浏览器发送一个http请求,然后首先会解析域名(主机名),然后在本地的"c:\windows\system32\drivers\etc\hosts"文件下查找域名(主机名)所对应的IP地址,如本地找不到就连接到万维网(外网)上查找所对应的IP地址,当查找到对应的I

浅谈NAT的原理、缺陷及其解决之道

为什么会有NAT 犹如windows统治着绝大部份桌面系统一样,TCP/IP也是网络协议中的实际统治者,而IP正是这个协议统治的基础,作为标识TCP/IP网络中每个结点的IP地址,由于它只有32个bit的空间,随着网络的发展,它变得越来越稀有,再加上IP地址地区分配的极端不平衡,已使一些地区比如亚洲部分国家的IP资源很快就会用完,在IPv6还没有普及开来之时,为了解决IP地址的燃眉之急,我们需要一种手段来尽量减少对公网IP的使用,这种手段就是NAT(Network Address Transla

[iOS]浅谈NSRunloop工作原理和相关应用

一. 认识NSRunloop  1.1 NSRunloop与程序运行 那么具体什么是NSRunLoop呢?其实NSRunLoop的本质是一个消息机制的处理模式.让我们首先来看一下程序的入口——main.m文件,一个ios程序启动后,只有短短的十行代码居然能保持整个应用程序一直运行而没有退出,是不是有点意思?程序之所以没有直接退出是因为UIApplicationMain这个函数内部默认启动了一个跟主线程相关的NSRunloop对象,而UIApplicationMain这个函数一直执行没有返回就保存

浅谈P2P终结者原理及其突破

P2P终结者按正常来说是个很好的网管软件,但是好多人却拿它来,恶意的限制他人的流量,使他人不能正常上网,下面我们就他的功能以及原理还有突破方法做个详细的介绍! 我们先来看看来自在网上PSP的资料:P2P终结者是由Net.Soft工作室开发的一套专门用来控制企业网络P2P下载流量的网络管理软件.软件针对 目前P2P软件过多占用带宽的问题,提供了一个非常简单的解决方案.软件基于底层协议分析处理实现,具有很好的透明性.软件可以适应绝大多数网络环境,包 括代理服务器.ADSL路由器共享上网,Lan专线等

浅谈鸽巢原理的证明和简单应用

一.鸽巢原理的证明 1.定义: 若有n个鸽巢和kn+1只鸽子,所有的鸽子都进入鸽巢,那么至少有一个巢中有k+1只鸽子(n,k≥0). 2.证明(反证法): 若每个鸽巢中的鸽子数都不大于k,则总鸽子数<=kn,与已知相悖.得证. 3.拉姆齐(Ramsey)定理的证明:6个人中,要么存在三个人彼此互相认识,要么存在三个人彼此都不认识: 证明:设六个人为六个点,认识或不认识用两种不同颜色的线段代表,因为两人只有一种关系,所以任意一点一定会引出连向其他5点的五个线段,根据鸽巢定理,有2种关系,有2*2+

浅谈 pid的原理与差异

pid  官方语言就是:比例   积分 微分.究其本质意义,比例到底是什么,原理是什么,这三个到底如何在物理世界这种运作的,大概了解的人又很少.过惯了拿起数据公式无脑推的日子的人更是如此,数学公式是很有用的,大部分不会质疑,也不会思考为什么这样,也没人问数学本质是什么?这样脱离实际 在运用实际情况的时候,就是一头雾水,公式如果起作用就可以,补齐作用就无脑推,借助公式顺口溜什么的,往往会比较头大. 今天不会讨论数学是什么,也不讨论宇宙的起源...只是讲解一下我对pid的理解和实际运用中的理解,当然