读书笔记-java虚拟机学习(一)

来源:互联网 发布:淘宝买苹果主板靠谱吗 编辑:程序博客网 时间:2024/06/05 10:19

一、写在前面

      近来,终于忙完一些琐碎的事情,开始做一些自己想做的事情,特别是补充一些多线程,并发编程方面的知识,最后发现,居然都或多或少指向一个共同的地方:java虚拟机。于是痛下决心,决定花一段时间,专门研究一下JVM相关的东西,于是就找了那本经典著作《深入理解JAVA虚拟机》作为核心,俗话说“好记性不如烂笔头”,所以这次,我就采取边学习边记笔记的方式来开展。当然,个人资质尚浅,能力有限,JVM本是有“深度的艺术”,所以有时我的理解可能不够成熟,甚至偏颇,希望大家多多包涵。也希望此行能有所收获。
      本文多为读书笔记,必要的时候,会写一些验证性质的代码。文中有的话语可能会直接引用原文(应该不会出现版权问题吧,哈哈,个人学习而已,不涉及商业元素)。
      好了,废话不多少了,开始JVM之旅吧。

二,关于JVM的内存区域

2.1 JVM运行时的数据区域

      本篇主要简要讲述了JVM的数据区域的划分和用途。
      java虚拟机在执行java程序的时候,它会将它自身所管理的内存划分为不同的数据区域,它们的用途各不相同,生命周期也不一样,有的在jvm启动的时候就已经存在了,有的是伴随着我们用户线程的启动而创建,线程的结束而销毁。在《java虚拟机规范》的规定中,java虚拟机管理的内存会包括下面几个数据区域,见下图2-1所示:
内存区域图

2.1关于程序计数器

      程序计数器是一块比较小的内存空间,你可以把它当成当前线程所执行字节码的指示器,也就是说,指针指向的地方,就是当前CPU执行到的地方。我们程序中的分支、循环、跳转、线程恢复等等,都依赖于程序计数器的支持。
      在java虚拟机中,多线程是通过线程轮流切换并分配处理器执行时间的方式完成的。我们知道,在同一个时刻,我们的单个CPU只会执行一个线程的指令,所以,为确保线程切换后还能还原到挂起时的位置,那么每个线程都需要一个独立的计数器,它们之间互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。(注:如果当前线程执行的是java程序,那么程序计数器指向的是正在执行的JVM字节码指令的地址;如果执行的是Native方法(什么是Native方法可参见:http://ghsea.iteye.com/blog/72543)这个计数器就为空(Undefined)。这个区域是唯一一个JVM没有规定任何OutOfMemoryError情况的内存区域。

2.3 JVM栈

      java虚拟机栈也是线程私有的,它的生命周期和线程相同。每个方法在执行的时候,都会创建一个栈帧,它的主要作用是存储局部变量表,操作数栈,动态链接,方法出口等信息。
局部变量表存放了编译期可知的各种基本类型的数据(boolean、byte、char、short、int、float、long、double)、对象引用(在不同的虚拟机对此的实现可能不一样,引用不同于对象本身,它可能是一个指向对象起始地址的指针,也可能是指针对象的句柄或者与此对象相关的位置)
在此内存区域,JVM规范中规定了两种异常,一种是当线程请求的栈深度大于虚拟机所允许的深度时,会抛出StackOverFlowError,当然现在虚拟机大多支持扩展,但如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

2.4 java 堆

      java堆是JVM所管理的内存中最大的一块。堆内存是被所有进程共享的一块内存,它在虚拟机启动的时候创建,堆内存中唯一的目的就是存放实例对象,几乎所有的实例对象都在这里进行分配。它也是垃圾收集器管理的主要区域,也叫作“GC堆”。
      现在垃圾收集器基本都采用分代收集算法,java堆中可以分为“新生代”和“老年代”。
      java虚拟机规范规定,java堆可以处于物理不连续的内存空间中,只要逻辑上连续就可以。当然,对内存也可以扩展,当堆中没有内存完成实例对象的分配,就会抛出“OutOfMemoryError”异常。

2.5 方法区

      方法区和堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等,JVM规范把方法区描述为堆的一个逻辑部分,但是它的别名叫做“Non-Heap”,目的是将方法区和java堆去分开。

2.6直接内存

      直接内存并不是JVM运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是和一部分被频繁地使用,也可能会导致OutOfMemoryError异常。
      我们知道,在JDK1.4中,引入了NIO(New Input/OutPut)类,它引入了一种基于通道与缓冲区的I/O方式,它可以使用Native库函数直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。所以在一些场合下,能显著提高性能,因为它避免了数据在java堆和Native堆中来回复制。
      上面简单说了一些我们需要知道的在JVM中的内存区域的作用,这是学习JVM的第一步,千里之行始于足下,希望自己能将这件事做好。坚持下去,我也相信,只要保持好奇的心,保持对编程的热忱,坚持下去,总能将事情做好的。晚安。。。

0 0
原创粉丝点击