【第一次发文】深入理解JVM(第二版)

第一篇 自动内存管理机制

这里原书中分了两个模块来写,我大概就讲讲自己目前所吸收到的:

1.JVM内存管理简述:以java中对象的生命周期为引子,来讲述更为形象;
2.运行时数据区域:抽象得去理解;

1. JVM内存管理简述

由于虚拟机具有自动内存管理机制,我们的new操作不需要对应free/delete代码,不容易出现内存溢出和泄露的问题(这里其实我也不是很懂,感觉就像有人帮你回收不用的对象,不出意外空间肯定基本管够),但是如果出现了内存溢出或者泄露问题,你不懂虚拟机对内存的管理,那肯定就凉了呀。于是乎,写博第一天,先弄点基础的。

重点 我稍微总结了一下我们new一个对象所做的工作,也就是对象创建过程:
1.执行编译完成得到的中间代码,当虚拟机在碰到new指令之后,会首先去检查new指令的参数是否能在常量池中定位到一个类的符号引用(这个应该是在方法区Method Area),并检查该引用是否被加载、解析和初始化;如果没有,就必须执行类对应的加载过程;
2.完成检查后,虚拟机会为对象分配所需要的内存,这个内存大小是在类加载的时候确定的,然后我们的对象就会在java堆中被创建
这里我们要记住,我们的创建对象要访问的两个线程公有数据区域,就是方法区—它是存储与类有关的相关信息,然后是java堆—它所存储的是与实例对象相关的数据,so,接下来再一一介绍。

2. 运行时数据区

尽管这个图大家见过很多遍了,但我还是自己再画一遍:
在这里插入图片描述
OK,接下来就要对每个区都进行一下介绍:

2.1 程序计数器

可能是因为内容和功能简单,它每每被放在第一个来讲,但是它的功能确实非常重要的,它决定了你程序的走向,保障了多线程的实现。

因为它指向了我们当前执行指令所在的地址,并且由于多线程是通过线程轮流切换分配处理器执行时间实现,在恢复之前线程的执行位置时,需要每个线程都有自己的程序计数器,这就私有的原因。

它本身是一块很小的内存空间,它记录的内容是当前执行指令的“行号”----假如一个线程正在执行一个java方法,程序计数器记录的就是当前执行的虚拟机字节码指令的地址,但是如果这是一个本地(Native)方法,那么程序计数器的值为空(Undefined)。它是唯一一个在java虚拟机规范中没有规定OutOfMemoryError情况的数据区域(这个应该比较好理解)

2.2 java虚拟机栈(VM Stack)

这个数据区域,首先它是线程私有的,它的生命周期与线程是完全相同的。然后从功能上理解它:

虚拟机栈描述的是java方法执行的内存模型,每个方法都会创建一个栈帧(stack frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。!!每一个方法从调用到执行完成,就对应一个栈帧在虚拟机栈中入栈到出栈的过程这句话应该是书中的精髓了。这里也不细说,我大概感受到,咱们在调用方法的时候用的变量呀、引用呀基本上都在这个栈帧里了。书本后面章节会更详细。

局部变量表,存放了编译器可知的各种基本数据类型:八种基本数据类型(画下来加深印象,虽然和本文关系不大),对象引用(reference类型,它不同于对象本身,是能找到相应对象地址的引用指针)和returnAddress类型(指向一条字节啊指令的地址)。
在这里插入图片描述
局部变量表的内存空间在编译期间就已经分配好了,方法运行期间不会改变局部变量表的大小。java虚拟机规范中对这个区规定了两种异常情况,一种是线程请求栈深度大于虚拟机所允许深度,将会抛出StackOverflowError;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够内存,将会抛出OutOfMemoryError异常。

2.3 本地方法栈(Native Stack)

这个栈和虚拟机栈非常类似,它是用于实现Native方法执行的栈,所以我们基本可以把它类比成虚拟机栈。

2.4 java堆(Heap)

之前我们讲到,在对象创建过程中,对象的实例数据就是存储在这个数据区域的,可想而知,java堆对于程序而言的重要性。对于大多数应用而言,java堆是虚拟机管理的最大的一块。,它是被所有 线程共享 的一块内存区域,在虚拟机启动时创建。此内存区域的 唯一目的 就是 存放对象实例 !!这一点在java虚拟机规范中的描述是:所有对象实例以及数组都要在堆上分配。
原文: The heap is the runtime data from which memory for all class instances and arrays is allocated.
但是随着JIT编译器(Just in time,即时)的发展和逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化(这些原文中的内容我感觉以后应该也会学到,我先摘录下来),所有对象都分配在了堆上面也就变的不那么绝对。

由于存放着所有的对象实例,那么GC(Garbage Collection)也理所当然主要在这一块区域上工作,所以也常常称之为“GC堆”,但是这里不细说区域分配以及回收细节的内容。

根据java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间,只要逻辑上连续即可。在实现的时候,既可以做成固定大小的堆,也可以做成可扩展的堆,不过当前主流的虚拟机都是可扩展的(利用参数-Xms和-Xmx来控制)。它也会存在OutOfMemoryError异常的情况。

2.5 方法区(Method Area)

方法区和堆的共同点,就是它们都是线程所共有的存储区域。它主要用于存储已被虚拟机加载的类的相关信息、常量、静态变量、即时编译器编译后的代码等数据。对于习惯在HotSpot虚拟机上开发,部署程序的开发者来说,很多人都更愿意把方法区成为“永久代”(Permanent Generation),但实际上两者不等价,仅仅是因为HotSpot团队选择把GC分代收集扩展到方法区,这样HotSpot虚拟机的垃圾收集器就可以像管理java堆一样来管理这部分内存,能够省去为方法区编写专门内存管理的代码。

书中提到一个内容,永久代为何慢慢不在使用?由于永久代可以像堆一样被GC所管理,但是呢永久代多出了一个上限,这样的话就会更容易出现内存溢出(这里我的感觉就是,由于要使用GC来管理,那么就相应得要给永久代设置一个上限,和算法有关吧,这样的话溢出出现就会异常,但是没有上限就是剩下的那些内存都是可以用的)。

和堆相比较而言:它和堆一样不需要连续的内存,可以选择固定大小或者扩展大小。它还可以选择不事先垃圾回收,因为在这个区域,很少会发生垃圾回收这个行为,但是并非数据放入该区域就会“永久”。该区域的内存回收主要是针对常量池的回收以及类的卸载。尤其是类型的卸载,条件是很苛刻的,在Sun公司的BUG列表中,曾出现过若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完全回收导致的内存泄露。(不知道啥时候自己能碰到这种BUG,后面要解释一下内存泄露和内存溢出的区别)

2.6 运行时常量池(Runtime Constant Pool)

它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息。这里为了研究一下.class文件内容究竟有哪些,我们使用一下JDK自带的javap工具在DOS命令窗口查看一下class文件的内容:

这里有一些类的描述信息、常量池(Constant pool)
在这里插入图片描述

在这里插入图片描述
后面那些别的,我觉得咱们大概知道就行了,我如果还有学习兴趣,也会想接着看的,不过暂时没有了。

这部分内容就是在类加载后进入方法区的运行时常量池了!!一般来说,除了把这些符号引用放入运行时常量池,还会把翻译出来的直接引用也存储在里面。

而且运行时常量池的一个重要特征是它具备动态性,有些常量在运行时也能产生,所以并非是预置入Class文件的常量才能进入运行时常量池,运行期间产生的也能放入池中,开发人员用的比较多的就是String类的intern方法。

2.7 直接内存

这个内存并不是jav虚拟机运行时数据区的一部分,但是它会被频繁地使用,而且也能导致OOM异常出现,所以书中也对它进行了讲解。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(buffer)的I/O方式,它可以使用Native函数库中的函数直接分配堆外内存,然后由堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在java堆和Native堆中来回复制数据。

显然,直接内存所受的限制不来自java堆,而是本机总内存,所以我们在设置虚拟机内存参数时往往会忽略掉直接内存,从而导致动态扩展出现OOM问题。

第一次写博,主要目的是开启一种新的学习方式,也当是笔记了,希望自己能坚持。望大佬批评指正!在把书上内容截取下来的同时,当然还是有自己思考的,我觉得主要还是要看实践,我自己做了创建对象和测试各个区域内存溢出的实验,期待自己后面的博文吧

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值