浅谈JVM

问:为什么要学习java虚拟机,它可以做什么?
答:java不像c、c++一样,不需要为每一个new写配对的delete/free代码,在JVM自动内存管理机制的帮助下,不容易出现内存泄漏和内存溢出的问题。但是,一旦出现,如果不了解虚拟机使用内存的原理,那排错就会变得异常艰难。
这里主要给介绍三个方面:

JVM运行时的数据区域

在这里插入图片描述
1.程序计数器(线程私有):是一块较小的内存空间,字节码解释器根据这个计数器的值来选取下一条需要执行的字节码指令。若线程执行的是java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果是Native方法(c++实现),这个计数器值则为空(Undefined),并且它是唯一一个不会出现内存溢出(OutOfMemoryError)的情况。
2.Java虚拟机栈(线程私有):每个java方法执行的时候都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。此区域会出现两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(目前大部分java虚拟机都可动态扩展),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
3.本地方法栈:与Java虚拟机栈作用相似,它是为本地方法即Native方法服务,抛出异常与虚拟机栈相同。
4.Java堆(线程共享):(Java Heap)是虚拟机管理的内存中最大的一块。虚拟机启动时就被创建。它的唯一目的时存放对象实例。规范中描述:所有的实例对象以及数组都要在堆上分配…java堆也是垃圾收集器管理的主要区域,很多时候也被称作“GC堆”(Garbage Collected Heap)。收集器基本采用分代收集算法,所以java堆中可细分为:新生代和老年代。java堆可以像我们的磁盘空间一样,可以处于物理上不连续的内存空间,只要逻辑上连续即可。当前主流虚拟机可通过-Xmx和-Xms控制实现扩展。
5.方法区(线程共享):用于存储被蓄奴籍加载的类信息、常量、静态变量、及编译后的代码等数据。当方法去无法满足内存分配需求时,将抛出OutOfMemoryError异常。
6.运行时常量池:是方法区的一部分,Class文件中除了有类的版体、字段、方法、接口等信息外,还有常量池,用于存放编译器生产的各种字面量和符号引用,类加载后进入方法去的运行时常量池中存放。运行时常量池相对于Class文件常量池具备动态性,在运行期间也可能将新的常量放入池中。受到方法区内存限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

类加载机制

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。其中验证、准备、解析统称为连接。在这里插入图片描述
加载过程:虚拟机需要完成
1).通过一个类的全限定名来获取定义此类的二进制字节流。
2).将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3).在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
验证过程:
1).文件格式验证:是否以魔数0xCAFEBABE开头;主、次版本号是否在当前虚拟机处理范围之内…(验证的东西有很多)
2).元数据验证:此类是否有父类;这个类的父类是否继承了不允许被继承的类(final修饰)…
3).字节码验证:最复杂的一个阶段,保证任意时刻操作数栈的数据类型与指令代码序列都能够配合工作;保证跳转指令不会跳动到方法体意外的字节码指令上;保证方法体中的类型转换是有效的…
4).符号引用验证:通过字符串描述的全限定名是否能找到对应的类…
准备阶段:
正式为类变量分配内存并设置类变量初始值的,变量所使用的内存都将在方法区中进行分配。
解析阶段:
虚拟机将常量池内的符号引用替换为直接引用。需要做类或接口的解析、字段解析、类方法解析、接口方法解析。
初始化阶段:开始执行字节码代码。

双亲委派模型

Java程序会用到3种系统提供的类加载器
●启动类加载器(Bootstrap ClassLoader)
●扩展类加载器(Extension ClassLoader)
●应用程序类加载器(Application ClassLoader)。
在这里插入图片描述
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己区尝试加载这个类,而是把请求委派给父亲加载器去完成,每一个层次的类加载器都是如此,知道传到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass() 方法之中,先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass)方法进行加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值