上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了。这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状态和激情都要慢慢褪去了,总觉得心里慌的一逼,这怎么行呢?!趁明天周末,在公司电脑上记录下这周的一些学习内容。近期在看一本很经典的java书籍:《深入理解java虚拟机 第二版》,几年前也翻过,但那时候功力不够,不太能看懂就没看了。现在回过头来看,发现确实写的很好,很多知识点都能理解了,而且讲的也很有深度,收获颇多。后期计划按照这本书的内容写出一系列博客,来深入学习和复习下java虚拟机相关的知识。

     一、JVM内存模型概述

              JVM内存模型其实也挺简单的,这里先提2个知识点:

1、组成:java堆,java栈(即虚拟机栈),本地方法栈,方法区和程序计数器。

2、是否共享:其中方法区和堆区是线程共享的,虚拟机栈,本地方法栈和程序计数器是线程私有的,也称线程隔离的,每个区域存储不同的内容。这2个知识点必须牢记,是掌握JVM内存模型的基础。

                           

      二、程序计数器

JVM中的程序计数器是一块很小的内存区域,但是这块内存区域挺有意思的。主要特性有3个:

1、存储内容:对于java普通方法(即没用native关键字修饰的方法),存储的是执行过程中当前指令的地址,而对于native方法,这里是空的(undefined),为啥呢?因为调用本地方法的时候可能已经超出了JVM虚拟机的内存地址了。

2、线程私有的:为什么程序计数器是线程私有的?根据存储内容也好理解,假如是线程共享的,那多个线程执行的时候,都不知道自己当前线程执行的地址是哪个了,有的线程快,有的线程慢,快的执行完就进入下一步,等慢的线程执行完回来发现自己的地址都变了,岂不乱套?

3、是JVM中唯一不会报内存溢出(OutOfMemoryError)的区域。

      三、虚拟机栈

              虚拟机栈主要存储的是一个个栈帧,每个栈帧中存储的是局部变量表,操作数栈,动态链接和方法出口信息等。其中局部变量表中存储的是方法中定义的一些局部变量,对象的引用,参数,和方法的返回地址等。局部变量表所占用的空间大小在编译期就能确定,在方法运行的时候,并不会改变局部变量表的空间大小,这结合局部变量表存储的内容就很好理解。操作数栈可以理解为对当前操作的数据入出栈,对于64位长度的long和double类型,每个操作数占用2个字宽(slot),其他类型的操作数占用一个字宽(slot)。每个方法调用时都会创建一个栈帧,执行的过程对应的就是一个栈帧在虚拟机栈中从入栈到出栈的过程。有关栈帧的内容可以参考一个网友写的一篇博客:https://blog.csdn.net/xtayfjpk/article/details/41924283,讲的很好很详细。这里放个栈帧的图,看了一目了然。

关于虚拟机栈内存溢出有2种情况:

1、线程请求的栈深度 超过了虚拟机允许的深度,会抛出StackOverflowError,所以当我们在代码中看到这个异常时,就应该想到可能是虚拟机栈出了问题。

2、如果虚拟机栈可以动态扩展(当前大部分JVM都可以动态扩展,不过JVM也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时,会抛出OutOfMemoryError异常。

      四、本地方法栈

           这块知识点比较简单,本地方法栈和虚拟机栈的功能类似,只不过是为JVM调用native方法时服务的,而且JVM对本地方法使用的语言(比如Java调用C语言实现的功能,就需要定义native方法来实现)、使用方式和数据结构都没有强制规定,因此不同的虚拟机可以自由实现。而且HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。与虚拟机栈类似,本地方法栈也会抛出StackOverflowError和OutOfMemoryError。

      五、方法区

方法区是一个比较重要的区域,java虚拟机规范中把方法区描述为堆的一个逻辑部分,但是为了和Heap(堆区)对应,也称Non-Heap(非堆区)。主要存储的是静态变量,常量(包括运行时常量),类的加载信息和java编译后的代码。这部分空间不需要连续,可以选择固定大小和可扩展,通常在这部分是没有GC的,因为GC回收的都是些静态变量,常量和类的加载信息,这些对象回收效果通常不尽人意,因此可以选择不实现垃圾回收。这块区域也称为持久代,当这块内存不足时,也会报OutOfMemoryError异常。

      六、堆区

         Java堆区是JVM内存中最胖的一块区域,因为这里存储的都是对象的实例和数组对象。这块区域是线程共享的,在JVM启动时就会创建,想想如果这么大的空间是线程私有的,那内存不得爆掉吗?按照java虚拟机规范,堆区的内容可以物理上不连续,只要逻辑上连续即可,在实现时可以是固定大小的,也可以是可扩展的,而且通常都是可扩展的,我们常用的内存参数-Xms和-Xmx就是用来调节堆大小的。java堆区按生命周期不同,分为新生代和老年代。新生代又可以细分为Eden和Survivor区,而Survivor又可以细分为Survivor1和Survivor2,这两者通常只使用其中一块,另一块用来GC时保留存活的对象。大部分的new出来的对象都是存放在Eden区,如果是大对象,比如一个很大的数组或者List对象,可以通过JVM参数-XX:PretenureSizeThreshold将超过指定大小的对象直接存入到老年代,需要注意的是,写程序时应该尽量避免朝生夕死的大对象进入老年代,因为相比年轻代的GC,老年代GC的成本更大。Eden和Survivor的默认大小比值的8:1:1,新生代默认的GC算法是复制算法。老年代的默认GC算法是标记整理法。关于这2种GC算法,会在下篇博客讲解。

当堆中没有足够内存时,会抛出OutOfMemoryError异常。关于堆区的内存模型,可以参考下面的图片:

深入理解java虚拟机学习笔记(一)JVM内存模型的更多相关文章

  1. 深入理解java虚拟机学习笔记(二)垃圾回收策略

    上篇文章介绍了JVM内存模型的相关知识,其实还有些内容可以更深入的介绍下,比如运行时常量池的动态插入,直接内存等,后期抽空再完善下上篇博客,今天来介绍下JVM中的一些垃圾回收策略.        一. ...

  2. 深入理解Java虚拟机 - 学习笔记 1

    Java内存区域 程序计数器 (Program Counter Register) 是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过 ...

  3. 深入理解Java虚拟机学习笔记(一)-----Java内存区域

    一 概述 对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为内一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题 ...

  4. 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制

    第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...

  5. 深入理解Java虚拟机学习笔记(二)-----垃圾收集器与内存分配策略

    写在前面 本节常见面试题: 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好处). 如何判断一个常量是废弃常量 如何 ...

  6. 深入理解java虚拟机学习笔记(二)

    第三章 垃圾收集器与内存分配策略 概述 ​ 程序计数器.虚拟机栈.本地方法栈3个区随线程而生,随线程而灭.因此大体上可认为这几个区域的内存分配和回收都具备确定性.在方法/线程结束时,内存自然就跟着回收 ...

  7. 深入理解java虚拟机学习笔记(一)

    第二章 Java内存区域与内存溢出异常 运行时数据区域 程序计数器(Program Counter Register) 程序计数器:当前线程所执行的字节码行号指示器.各条线程之间计数器互不影响,独立存 ...

  8. 类加载机制(深入理解JAVA虚拟机学习笔记)

    1.类加载机制的定义 将class文件加载到内存,然后对class文件中的数据进行校验.解析和初始化,转换成可以被虚拟机直接使用的JAVA类型,这就是虚拟机的类加载机制.(在JAVA中,类的加载.连接 ...

  9. 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域

    深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...

随机推荐

  1. 使用mongoosejs链接Mongodb

    以前只是了解Nodejs 这回打算好好学学.学到熟练使用的程度 var options={ user:'test_user', pass:'123456' }; mongoose.connect('m ...

  2. stm32 独立看门狗学习

    STM32F10xxx内置两个看门狗,提供了更高的安全性.时间的精确性和使用的灵活性.两个看门狗设备(独立看门狗和窗口看门狗)可用来检测和解决由软件错误引起的故障. 独立看门狗(IWDG)由专用的低速 ...

  3. Oracle之RMAN备份及还原

    RMAN可以进行增量备份:数据库,表空间,数据文件 只有使用过的block可以被备份成backup set 表空间与数据文件对应关系:dba_data_files / v$datafile_heade ...

  4. 为什么地址栏的快捷键是Alt D

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:为什么地址栏的快捷键是Alt D.

  5. Java中的阻塞队列-LinkedBlockingQueue(二)

    原文地址:http://benjaminwhx.com/2018/05/11/%E3%80%90%E7%BB%86%E8%B0%88Java%E5%B9%B6%E5%8F%91%E3%80%91%E8 ...

  6. 如何在Chrome粘贴图片直接上传

    背景 截图或页面复制图片,可以直接通过Ctrl+v 粘贴上传图片 原理 操作:复制(截图)=>粘贴=>上传 监听粘贴事件=>获取剪贴板里的内容=>发请求上传 浏览器:Chrom ...

  7. Struts2_Struts标签大致内容

    Struts-Tags1.通用标签 a) property b) set i.默认为 action scope,会将值放入 request 和 ActionContext中 ii. page.requ ...

  8. 当你的域名是数字开头时如何命名java包路径

    例如:域名是1001y.net 理想的包路径是net.1001y,但由于java命名规范的问题,首字母不能为数字,这时我们只有两种选择: 1,net.$1001y 使用$符号作为首字母. 2,net. ...

  9. DB2安装教程图解

    下载好之后,是exe文件,但是双击后基本上都是解压,但是使用自身的解压的话会有很多文件解压失败的情况,所以推荐使用自己电脑上自带的解压工具直接解压(如360解压,好压等). 解压之后直接运行setup ...

  10. Linux CentOS下部署Java Web项目

    本文讲解如何在Linux CentOS下部署Java Web项目的步骤. 一.环境准备: (1)Linux CentOS (2)apache-tomcat-9.0.10 (3)XShell 二.启动t ...