Java基础知识强化100:JVM 内存模型
一、 JVM内存模型总体架构图:
方法区和堆由所有线程共享,其他区域都是线程私有的
二、 JVM内存模型的结构分析:
1. 类装载器(classLoader)
类装载器,它是在java虚拟机中用途是把类从本地文件中装载到系统内存(运行时数据区)中。
类装载器装载本地文件到系统内存中的步骤:
• 装载 :查找并装载类的二进制数据。
• 链接 :执行验证 准备 解析(非必要)。
• 初始化 :把类型变量初始化为正确的变量。
2. 运行时数据区
(1)方法区(Method Area)
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码等信息。
方法区是线程间共享的,当两个线程同时需要加载一个类型时,只有一个类会请求ClassLoader加载,另一个线程会等待。
对于每一个加载的类型,会在方法区中保存以下信息:
- 类及其父类的全限定名(java.lang.Object没有父类)
- 类的类型(Class or Interface)
- 访问修饰符(public, abstract, final)
- 实现的接口的全限定名的列表
- 常量池
- 字段信息
- 方法信息
- 除常量外的静态变量
- 一个到ClassLoader的引用
- 一个到Class类的引用
对于每一个字段,会在方法区中保存以下信息(字段声明顺序也会保存):
- 字段名
- 字段的类型
- 字段的修饰符(public, private , protected, static, final, volatile, transient)
对于每一个方法,会在方法区中保存以下信息(方法声明顺序也会保存):
- 方法名
- 方法返回类型(或void)
- 参数信息
- 方法修饰符(public, private, protected , static, final, synchronized, native, abstract)
如果方法不是抽象方法并不是本地方法(Native Method),还会保存以下信息:
- 方法的字节码
- 本地变量表及操作数栈的大小
- 异常表
(2)堆(Heap)
虚拟机中用于存放对象与数组实例的地方,垃圾回收的主要区域就是这里(还可能有方法区)。如果垃圾收集算法采用按代收集(目前大都是这样),这部分还可以细分为新生代和老年代。新生代又可能分为Eden区,From Survivor区和To Survivor区,主要是为了垃圾回收。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。Java堆只要求逻辑上是连续的,在物理空间上可以不连续。
新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存得足够长,老年对象就会被移入老年代。
新生代又可进一步细分为eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。刚创建的对象都放入eden,s0和s1都至少经过一次GC并幸存。如果幸存对象经过一定时间仍存在,则进入老年代(tenured)。
(3)程序计数器(Program Couter Register)
它总是指向下一条要执行的指令地址。
类似于PC寄存器,是一块较小的内存区域,通过程序计数器中的值寻找要执行的指令的字节码,由于多线程间切换时要恢复每一个线程的当前执行位置,所以每个线程都有自己的程序计数器。这一个区域不会有OutOfMemeryError。当执行Java方法时,这里存储的执行的指令的地址,如果执行的是本地方法,这里的值是Undefined。
(4)虚拟机栈(VM Stack)
虚拟机栈是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,虚拟机栈表示Java方法执行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这个方法执行完后,就会弹出相应的栈帧。
如果请求的栈的深度过大,虚拟机可能会抛出StackOverflowError异常,如果虚拟机的实现中允许虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出OutOfMemoryError异常。
(5)栈帧(Stack Frame)
栈帧分为三部分:局部变量区(Local Variables)、操作数栈(Operand Stack)和帧数据区(Frame Data)。
• 局部变量区(Loca Variables)
局部变量区包含方法的参数和局部变量。局部变量区被组织一个一个从0开始的字数组,byte、short、char在存储前被转换为int,boolean也被转换为int,0表示false,非0表示true,long和double占据两个字长。
• 操作数栈(Operand Stack)
操作数栈相当于cpu的通用寄存器,java虚拟机没有pc寄存器,而是使用栈,Java指令所使用操作数都是从操作数栈中得到。操作数栈也被组织为一个字数组,但不同于局部变量区,它不是通过数组下标访问的,而是能过栈的Push和Pop操作,前一个操作Push进的数据可以被下一个操作Pop出来使用。
• 帧数据区(Frame Data)
通常它都被用来实现常量池解析和异常处理等。
这部分的作用主要有三部分:
常量池中数据的解析
方法执行完后处理方法返回,恢复调用方现场
方法执行过程中抛出异常时的异常处理,存储有一个异常表,当出现异常时虚拟机查找相应的异常表看是否有对应的Catch语句,如果没有就抛出异常终止这个方法调用
(6)本地方法栈(Native Method Stack)
和虚拟机栈功能相似,但管理的不是JAVA方法,是本地方法,本地方法是用C实现的。
任何本地方法接口都会使用本地方法栈
Java调用本地方法和Java方法的执行示例:
(7)直接内存
直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小。
3. 执行引擎
把内存中的数据根据指令集合执行对应的操作。
三、小结:
1. 方法区和堆区是线程间共享的
(1)所有的jvm实例都有一个方法区和堆区,它们是jvm实例中所有的线程共享的,所以其中的数据要考虑到锁的问题。
(2)如果两个线程同时用到一个对象,并且该对象没有加载到内存中,那么只能有一个线程来加载该类,另一个线程等待。
2. Java栈和PC寄存器是线程内共享的
3. 对象的访问
首先我们看这样一句简单的代码:
Object obj = new Object();
假设上面这句代码出现在方法体中,其中"Object obj" 这部分的语义将会反映到Java栈的本地变量表中,作为一个引用类型数据出现。而"new Object()"这部分的语义将会反映到Java堆之中,形成一块存储了Object类型所有实例数据值(Instance Data:对象中各个实例字段的数据)的结构化内存。根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。
另外,在Java堆中还必须包含能查找此对象类型数据(如:对象类型,父类,实现接口,方法等)的地址信息,这些类型数据则存储在方法区之中。
(1)使用句柄访问方式:Java堆之中将会划分一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息,如图:
(2)直接指针访问方式:reference变量中直接存储的就是对象的地址,而java堆对象一部分存储了对象实例数据,另外一部分存储了对象类型数据。如图:
这两种访问对象的方式各有优势,使用句柄访问方式最大好处就是reference中存储的是稳定的句柄地址,在对象移动时只需要改变句柄中的实例数据指针,而reference不需要改变。使用指针访问方式最大好处就是速度快,它节省了一次指针定位的时间开销,就虚拟机而言,它使用的是第二种方式(直接指针访问)。
Java基础知识强化100:JVM 内存模型的更多相关文章
- Java基础知识强化100:jsp和servlet有什么区别
首先你先要弄懂什么是servlet,servlet是在服务器端执行的java程序,只不过它有专门的一套规则(就是我们平常所说的api):jsp说得简单点就是用另一套简单的规则写的servle ...
- Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介
1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...
- JAVA高级篇(二、JVM内存模型、内存管理之第二篇)
本文转自https://zhuanlan.zhihu.com/p/25713880. JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...
- JAVA高级篇(二、JVM内存模型、内存管理之第一篇)
JVM内存结构如 Java堆(Heap),是Java虚拟机所管理的内存中最大的一块.Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 ...
- Java基础知识强化之多线程笔记01:多线程基础知识(详见Android(java)笔记61~76)
1. 基础知识: Android(java)学习笔记61:多线程程序的引入 ~ Android(java)学习笔记76:多线程-定时器概述和使用
- Java基础知识强化48:Java中哈希码
1.概念: 哈希其实只是一个概念,没有什么真实的指向.它的目的是保证数据均匀的分布到一定的范围内.所以不同数据产生相同的哈希码是完全可以的. 现在是站在JAVA虚拟机的角度来看内存 ...
- Java基础知识强化之多线程笔记05:Java程序运行原理 和 JVM的启动是多线程的吗
1. Java程序运行原理: Java 命令会启动Java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程.该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 m ...
- Java基础知识强化之IO流笔记58:内存操作流
1. 内存操作流: 用来操作处理临时存储的信息的. (1)操作字节数组: ByteArrayInputStream ByteArrayOutputStream 代码示例: package cn.itc ...
- Java基础知识强化14:Java死亡竞赛题目解析
一个小型网站上发布了一个称为Java“死亡竞赛”的新项目.测验发布后,超过20000位开发者参加了测验.网站以20道关于Java的多选题为主.我们得到了众多开发者的测验统计数据,今天,我们非常乐意 ...
随机推荐
- HDU 5007 Post Robot
Post Robot Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- java并发之CountDownLatch、Semaphore和CyclicBarrier
JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatch.Semaphore和CyclicBarrier. CountDownLatch Java之CountDownLatc ...
- HDU 5701 中位数计数 暴力
老题了,附上黄学长链接一发,直接改改就AC了,http://hzwer.com/1216.html #include <cstdio> #include <iostream> ...
- HDU 1078 FatMouse and Cheese 记忆化搜索DP
直接爆搜肯定超时,除非你加了某种凡人不能想出来的剪枝...555 因为老鼠的路径上的点满足是递增的,所以满足一定的拓补关系,可以利用动态规划求解 但是复杂的拓补关系无法简单的用循环实现,所以直接采取记 ...
- 不区分大小写匹配字符串,并在不改变被匹配字符串的前提下添加html标签
问题描述:最近在搭建一个开源平台网站,在做一个简单搜索的功能,需要将搜索到的结果中被匹配的字符串添加不一样的颜色,但是又不破坏被匹配的字符串. 使用的方法是替换被匹配的字符串加上font标签.但是搜索 ...
- sqlserver 2008 R2 分区表测试
有一张表期中有100多w条数据 程序执行起来比较慢,想用分区表的办法,使查询变快一些. 方案如下 --查看分区信息SELECT * FROM sys.partition_range_values -- ...
- Linux Oracle碰到错误:ORA-27101: shared memory realm does not exist
从ITPUB上摘抄并已验证 1.实例没有启动 sqlplus /nologconnect / as sysdbastartup
- CSS换行:word-wrap、word-break和text-wrap区别
一.word-wrap:允许对长的不可分割的单词进行分割并换行到下一行.(中英文处理效果一样) word-wrap有两个取值: 1.word-wrap: normal:只在允许的断字点换行(浏览器保持 ...
- 【转载】sed命令详解
[转载自]http://www.cnblogs.com/edwardlost/archive/2010/09/17/1829145.html sed -i 把后面的操作后的文本输出回原文本 ...
- [iOS基础控件 - 6.7.1] 微博展示 代码
Controller: // // ViewController.m // Weibo // // Created by hellovoidworld on 14/12/4. // Copyrig ...