JVM笔记(一) Java内存区域
Java 内存区域
总概
java虚拟机在执行java程序的过程中,会把它管理的内存划分为几个不同的数据区域。每当运行一个java程序时,就会启动一个虚拟机。
具体的区域如图所示:
同时,方法区 与 堆 是由所有线程共享的数据区;而 虚拟机栈、本地方法栈、程序计数器 则是被线程隔离的区域。
一、程序计数器
什么是程序计数器?
概念:就是当前线程所执行的字节码的行号指示器。
- JVM的概念模型中,字节码解释器通过改变这个计数器的值来选取下一条字节码指令。
- JVM的多线程其实就是通过线程轮流切换并分配处理器执行时间的方式来实现的(在任何一个确定的时刻内,一个处理器都只会执行一条线程中的指令)。为了线程切换后能够恢复到正确的执行位置,每条线程都需要有独立的程序计数器,各线程计数器互不影响,独立存储。所以,程序计数器是线程私有的内存区域
- 如果线程执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Native方法,则计数器的值为空。
- Java虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。
二、Java虚拟机栈
- 线程私有,生命周期与线程相同。
- 虚拟机描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。(PS:我觉得可以将它看作是一个方法的快照,记录着方法的参数之类的信息,其实就是方法运行时的数据结构基础)
- 局部变量表,存放了各种基本数据类型、对象引用,和返回后所指向的字节码的地址。(PS:这个就是我们常说的“栈内存 Stack”)
- 在Java虚拟机规范中,对于该区域规定了两种异常状态:
- StackOverflowEError : 线程请求的深度大于虚拟机所允许的深度;
- OutOfMemoryError : 动态扩展时无法申请到足够的内存。
三、本地方法栈
- 本地方法栈为虚拟机使用到 Native 方法服务。
- 同 Java虚拟机栈 一样,会抛出 StackOverflowEError 和 OutOfMemoryError 异常。
四、Java堆(线程共享)
C语言是使用 malloc 从堆中来分配空间的。同样的,Java堆是用来存放对象实例的。
Java规范中的描述:所有的对象实例以及数组都要在堆上分配;但随着技术的发展,这种说法也不是那么“绝对”了。
- 唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配内存。
- Java堆是垃圾收集器管理的主要区域。
- 可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,同时也是可扩展的。
- OutOfMemoryError : 如果在堆中没有内存完成实例分配,并且堆也无法再扩展。
五、方法区(线程共享)
- 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 方法区对于垃圾回收的效果比较难以令人满意,尤其是对于类型的卸载条件相当坎坷,但对于该区域进行垃圾回收是必要的。
- OutOfMemoryError : 如果方法区无法满足内存分配需求就会抛出这个异常。
常量池
常量池是方法区的一部分。这里存放着编译期生成的各种字面量和符号引用(其实就是八大基本类型的包装类型和String类型数据)。
运行时常量池一个重要的特征:具备动态性,解释就是Java语言并不要求常量一定只有编译期才产生,比如String类的intern()方法。
- String.intern()
String类的intern()方法是一个Native方法,底层调用C++的 StringTable::intern方法实现。
public static void main(String[] args) {
//String str = "FunriLy";
String str1 = new StringBuilder("Funri").append("Ly").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("java").toString();
System.out.println(str2.intern() == str2);
}
运行上面的代码,会得到一true一false;若去掉注释,则会得到两个false。
* 调用intern()后,JVM就会在当前类的常量池中查找是否存在与str等值的String,若存在则直接返回常量池中相应Strnig的引用;若不存在,则会在常量池中创建一个等值的String,然后返回这个String在常量池中的引用。
* 在 JDK 1.6 版本,**常量池被保存在方法区(PermGen)中**,而String类对象存在于堆区中,这就意味着多次intern()操作会使内存中存在许多重复的字符串,会造成性能损失。同时,在此区域其大小会受到限制。
* 在 JDK 1.7 版本,开始了"永久代"的移除(也就是前面提到过GC的要求):符号引用转移到了本地方法栈;字面量转移到了堆;类的静态变量转移到了堆。
* 在iJDK 1.8 版本,去掉了PermGen内存,所以永久代的参数 -XX:PermSize 和 -XX:MaxPermSize 也被移除了,转而出现了一个元空间(Metaspace)。【关于这一点,可以看一下这篇博文:http://blog.csdn.net/zhyhang/article/details/17246223 】
六、对象的创建
- 当虚拟机遇到一条 new 指令时,首先去常量池中检查是否能定位到一个类的符号引用,并检查类是否已经被加载、解析和初始化过。如果没有,就执行相应的加载操作。
- 接下来就是在堆中分配空间了。有两种方案:
- 第一种(指针碰撞法)
假设堆中内存绝对规整,那么只要在用过的内存和没用过的内存间放置一个指针即可,每次分配空间的时候只要把指针向空闲空间移动相应距离即可。 - 第二种(空闲列表)
假设内存空间并不规整,通过维护一个列表来记录堆内存的使用情况(PS:操作系统对于内存的管理就是这种模式)。
- 第一种(指针碰撞法)
- 但是我们也要考虑在并发情况下的线程安全性问题。比如,正在给对象A分配空间,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存,导致对象AB占用了相同的一块空间。
- 第一种,对分配内存空间的动作进行同步处理。
- 第二种,每个线程在堆中预先分配一小块内存,称为本地线程分配缓存(TLAB),每个线程只在自己的 TLAB 中分配内存。
- 最后,对象被成功分配内存空间,虚拟机会对对象进行必要的设置,对象的类,对象的哈希码等信息都存放在对象的对象头中,所以分配的内存大小绝不止属性的总和。
七、对象的内存布局
对于大部分的虚拟机,对象在堆中的存储布局可以分为3块区域:
- 对象头,
包括两部分信息,第一部分用于存储对象自身运行时数据,例如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳,官方称为”Mark
Word”。另外一部分是类型指针,即对象指向它的类元数据指针(即指向方法区类数据的指针)。 - 实例数据,存放着对象真正存储的有效信息,包括了父类继承和子类定义的信息。
- 对齐填充,占位符作用。
八、对象的访问定位
引用存放在虚拟机栈中,数据类型为reference,对象实例存放在堆中。
Java程序就是通过栈上的reference数据来操作堆上的具体对象。目前,主流的访问方式有使用句柄和直接指针两种。
使用句柄来访问对象
使用句柄的话,将会在Java堆中划分一块区域作为句柄池,引用中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
优势:在对象被移动时(比如垃圾回收)只会改变句柄中的实例数据指针,而引用本身不需要修改。
通过指针来访问对象
使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而引用中存储的直接就是对象地址。
优势:访问速度快,节省了一次指针定位(访问对象是非常频繁的操作)的时间开销。
参考资料
- 《收入了解Java虚拟机》(周志明 著)
- (intern()方法)http://www.jianshu.com/p/95f516cb75ef
JVM笔记(一) Java内存区域的更多相关文章
- 【转载】Java系列笔记(3) - Java 内存区域和GC机制
Java系列笔记(3) - Java 内存区域和GC机制 转载:原文地址http://www.cnblogs.com/zhguang/p/3257367.html 目录 Java垃圾回收概况 Java ...
- Java系列笔记(3) - Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- JVM探秘:Java内存区域
本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 概述 Java 虚拟机为程序员分担了很多内存管理的工作,不再像 C/C++ 那样容易出 ...
- JVM参数配置 java内存区域
java内存区域 一些基本概念 http://www.importnew.com/18694.html https://www.cnblogs.com/wangyayun/p/6557851.html ...
- 【搞定Jvm面试】 Java 内存区域揭秘附常见面试题解析
本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...
- 深入理解JAVA虚拟机阅读笔记1——JAVA内存区域
一.Java内存区域 1.程序计数器 线程私有. 当前线程所执行的字节码的行号指示器.由于JAVA是多线程的,因此每个线程都独立的程序计数器. 异常:没有规定任何OutOfMemeryError情况的 ...
- 《深入java虚拟机》读书笔记之Java内存区域
前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,主要是方便之后进行复习. 运行时数据区域 Java虚拟 ...
- 【JVM.1】java内存区域与内存溢出
鲁迅曾说过:Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进来,墙里面的人想出去. 一.虚拟机内存分布 Java虚拟机在执行Java程序的过程中会把它所管理的内存 ...
- JVM 之:Java 内存区域与内存溢出
内存区域 Java 虚拟机在执行 Java 程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java 虚拟机规范将 JVM 所管理的内存分为以下几个运行时数据区:程序计数器.Java 虚拟机 ...
随机推荐
- C++微专业课程辅导(内存模型和动态内存)
“除了静态内存和栈内存之外,每个程序还拥有一个内存池.这部分空间被称作自由空间(free store)或堆(heap).程序用堆来存储动态分配(dynamically allocate)的对象”——& ...
- Nodejs -- 使用koa2搭建数据爬虫
当前爬虫项目开发所需中间件: cheerio: 则能够对请求结果进行解析,解析方式和jquery的解析方式几乎完全相同 cheerio中文文档 开发参考node - cheerio模块 superag ...
- 分页器的js实现代码 bootstrap Paginator.js
参考: http://www.jb51.net/article/76093.htm 如前所述, 不要什么都想到 jquery的 脚本js, 应该首先推荐的是 css 和 元素本身的事件 函数 如: o ...
- POJ 2752 Seek the Name, Seek the Fame(KMP中next的理解)题解
题意: 要求你给出每个前后缀相同的串的长度,比如: "alala"的前缀分别为{"a", "al", "ala", &q ...
- newcode wyh的吃鸡(优势队列+BFS)题解
思路: 要用优势队列,因为有的+2,有的+1,所以队列中的步长是不单调的,所以找到一个答案但不一定最小,所以用优势队列把小的放在队首. 要记录状态,所以开了三维,题目和昨天做的那道小明差不多 vis开 ...
- BZOJ3298: [USACO 2011Open]cow checkers 威佐夫博弈
Description 一天,Besssie准备和FJ挑战奶牛跳棋游戏.这个游戏上在一个M*N的棋盘上, 这个棋盘上在(x,y)(0<=x棋盘的左下角是(0,0)坐标,棋盘的右上角是坐标(M-1 ...
- Oracle SQL Developer 中配置JDBC驱动程序连接
此博客仅作为自己备忘,没有丝毫技术含量.把Postgres的JDBC驱动程序放在Oracle SQL Developer的JDBC的文件夹下,然后安装Oracle SQL Developer,添加新连 ...
- TensorFlow上实践基于自编码的One Class Learning
“我不知道什么是爱,但我知道什么是不爱” --One Class Learning的自白 一.单分类简介 如果将分类算法进行划分,根据类别个数的不同可以分为单分类.二分类.多分类,常见的分类算法主要解 ...
- 04_Windows平台Spark开发环境构建
Spark的开发环境,可以基于IDEA+Scala插件,最终将打包得到的jar文件放入Linux服务器上的Spark上运行 如果是Python的小伙伴,可以在Windows上部署spark+hadoo ...
- pandas 设置单元格的值
import pandas as pd import pickle import numpy as np dates = pd.date_range() df = pd.DataFrame(np.ar ...