设为首页 - 加入收藏 延边站长网 (http://www.0433zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: mini 系统 为什 为啥
当前位置: 首页 > 综合聚焦 > 资源网站 > 空间 > 正文

你真的了解JVM吗?

发布时间:2019-10-17 03:16 所属栏目:[空间] 来源:Klausss
导读:对于java程序员小白来说(没错,是我),jvm总是笼罩着一层神秘的面纱的,java是如何分配内存的,又是如何回收内存的呢?有人说内存管理是一道墙,墙里面的人想出去,墙外面的人想进去。而我们java程序员,就是硬着头皮进去的那群人... 学习的目的很简单 ----

对于java程序员小白来说(没错,是我),jvm总是笼罩着一层神秘的面纱的,java是如何分配内存的,又是如何回收内存的呢?有人说内存管理是一道墙,墙里面的人想出去,墙外面的人想进去。而我们java程序员,就是硬着头皮进去的那群人...

你真的了解JVM吗?

学习的目的很简单

-----知道jvm是什么东西,它是干什么的,以及它是怎么干的

-----知其为何,何其为之

-----夸夸其谈

-----装逼

把书读薄,把知识丰满。

本文旨在记录虚拟机学习过程中的一些理解和知识点,如果有幸能帮助到你,或许可以给你提供帮助,乐意之至。

jvm分区

由java虚拟机规范规定,将java虚拟机管理的内存分为以下几个运行时的数据区域(这只是规范上面的分区,具体不同的虚拟机会有不同的实现)。

你真的了解JVM吗?

其中方法区和堆(黄色块)是 所有线程共享 的区域,虚拟机栈,本地方法栈和程序计数器这三块是 线程私有。

的区域,各个线程之间互不影响,独立存储。

区域功能

  • 程序计数器:程序计数器是一块比较小的内存空间,主要用在基础的字节码指令、线程切换等功能上,java虚拟机的多线程是通过线程轮流切换来分配处理器时间的,也就是所谓的 时间片轮转调度法 ,那么当线程切换的时候如何恢复到正确的执行位置,就需要每个线程自己“记住”,这就是程序计数器一个很重要的作用。
  • java虚拟机栈:也叫做java方法栈,也就是通俗意义上大家讲的堆栈堆栈 的栈了。每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表、操作数栈、动态链接等信息,方法调用的时候入栈,方法结束的时候出栈。
  • 本地方法栈:本地方法栈与虚拟机栈的功能很相似,虚拟机栈执行的是java方法,本地方法栈执行的是Native方法。
  • java堆:上面讲过,堆是被所有线程共享的一块内存区域。堆用来存放对象的实例,几乎所有的对象实例和数组都在堆上分配,所以堆也是内存中 最大 的一块。
  • 方法区:方法区也是所有线程共享的区域,它通常用来存储加载的类信息、常量、静态变量等数据。有很多人也把它叫做 永久代 ,事实上这是两个东西,方法区是一个具体的 实现 ,而方法区是java虚拟机规范当中定义的一个分区 概念 ,仅仅是因为HotSpot虚拟机采用了分代的思想来进行GC,并且用永久代来实现了规范中的方法区,所以很多人混淆了两者。但是这个称呼可以在一定程度上反映这个区域的GC是比较少的,或者说GC条件是比较苛刻的。

java堆

堆是内存池中最大的一块区域,也是jvm垃圾回收最主要的区域,那么我们就来梳理一下堆的知识。

对象的创建过程:前面讲到,堆是用来存放对象实例的,那么一个对象到底是如何创建又是如何放入堆中的呢?

a). java中通常使用一个new关键字来创建对象,当虚拟机接收到new指令时,需要检查这个类是否已经被加载、解析和初始化,如果没有的话,需要进行相应的类加载过程。类加载的过程是分为多个阶段的,其中 初始化阶段 ,虚拟机严格规定了有且只有5种情况,属于对一个类进行 主动引用 ,必须立即堆类进行“初始化”,除此之外的所有引用都是被动引动,不会触发初始化操作。

b).虚拟机为对象分配内存,如果堆里面的内存是规整的,已经使用的内存放一边,空闲的内存放另一边,这种分配方式称为“ 指针碰撞 ”;如果内存不是规整的,那么虚拟机就要维护一个列表,记录哪些内存块是可用的,分配的时候需要从列表里面找到一块足够大的内存空间划分给对象,称为“ 空闲列表 ”

你真的了解JVM吗?

c).内存分配之后,将分配到的内存空间初始化为零值

d).此时在虚拟机看来,一个新对象已经创建完成了,java执行对应的init方法,按照程序进行初始化,得到一个真正可用的对象

对象的内存布局:对象可以分为3块区域,对象头,示例数据和对齐填充,对象头又由 Mark Word 和 类型指针组成

a).Mark Word 用于存储对象自身运行时数据(HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等)

b). 类型指针 即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

对象的定位:前面提到,java方法中对象的引用是存放在栈上的,而对象的实例是存放在堆上的。目前主流的访问方式有使用 句柄 和 直接指针 两种

a).句柄:java堆中划分出一块内存作为句柄池,reference中存储的是对象的句柄地址,句柄中含有两个地址,一个指向堆中这个对象的实例的地址,一个指向方法区中这个对象类型信息的地址

你真的了解JVM吗?

b).直接指针:reference中直接存储对象的实例地址,然后通过取到对象头中的类型指针来确定方法区中这个对象类型信息的地址。

你真的了解JVM吗?

c).比较:采用句柄的最大好处就是对象的实例地址在发生改变的时候,只需要更新句柄中指向对象实例的数据指针,不需要修改reference本身,但是可以看到相对比直接指针访问,句柄多了一次指针定位,这使得它的速度更慢。

java堆溢出:常见的就是OOM异常,导致堆溢出的原因通常有两个, 内存泄露 和 内存溢出。

a).内存泄漏:对象已经可以回收,但是却没有被回收。 那么问题来了,怎么判断一个对象是不是可以被回收或者说应该被回收呢?通常虚拟机是采用 可达性算法 来判定的,这个后面我会继续展开一下。

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章