JVM自动内存管理机制——Java内存区域(上)
一、JVM运行时数据区域概述
Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄漏和内存溢出的问题。显然,这里的不容易只是相对而言的,如果我们想要降低这种代码隐患的发生,就需要对Java虚拟机怎样使用内存有了解,这样的话就算产生错误,排查起来也会相对容易。下面我们来说一说JVM运行时数据区域
1、程序计数器(PC寄存器): 被看作是当前线程所执行的字节码的行号指示器,字节码解析的时候就通过改变这个计数器的值来选取下一条需要执行的指令(包括分支、循环、跳转、异常处理等等都依赖PC)。在多线程程序中,每条线程都需要拥有一个独立的程序计数器,所以程序计数器是线程私有的。(如果程序正在执行一个Java方法,那么这个计数器记录的就是当前执行的字节码指令的地址;如果执行的是一个Native方法,那么计数器为Undefined),该内存区域是JVM规范中唯一一个没有规定OutOfMemoryError的区域。
2、Java虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型,这块区域也是线程私有的,生命周期和线程相同。每个方法在执行的时候会创建栈帧,用来存放局部变量表、操作数栈、动态链接、返回值等信息。(我们常说的在Java中的栈内存就是指这块区域)。在JVM规范中:如果线程请求栈深度大于虚拟机提供的深度,那么抛出StackOutflowError异常;如果无法申请得到足够的内存,会抛出OutofMemoryError异常
3、本地方法栈:与Java虚拟机栈不同的是,本地方法栈为虚拟机执行Native方法服务,而Java虚拟机栈为虚拟机执行Java方法服务。由于JVM规范并没有对本地方法栈做强制规定,所以如同HotSpot一样,直接将本地方法栈和虚拟机栈合二为一。异常情况和JAVA虚拟机栈相同
4、Java堆:Java堆是被所有线程所共享的一块内存区域,在虚拟机启动时候创建,这块区域的目的就是存放对象实力,基本上我们所创建的所有的对象实例都要在这里分配内存。我们下面会详细的介绍Java堆
5、方法区:方法区和Java堆一样,是各个线程共享的内存区域,
6、运行时常量池:运行时常量池是方法区的一部分,在Class文件中(存在类的版本、字段、方法、接口等)存在常量池的信息,作用就是用于存放编译期间生成的各种字面量和符号引用。受到方法区内存的限制,当无法申请到内存时候会抛出OutOfMemoryError异常
还有一部分就是直接内存,但是直接内存并不是运行时数据区域的一部分,在Java的NIO库中允许Java程序员频繁的使用直接内存,从而提高性能(避免了Java堆和Native堆之间来回复制数据)。
二、再探Java堆、栈、方法区
1、Java堆:Java堆的存在是为了解决数据存储的问题,堆中存放了对象实例。堆也是垃圾收集器管理的主要区域(所以也可以被称为GC堆)。从内存回收的角度来讲,采用分代回收算法的JVM,在堆中可被分为新生代(新生的对象或者年龄不大的对象)和老年代(老年对象),这里面的划分是按照垃圾收集器的次数,来判断对象的年龄。新生代中又被分为Eden区、s0区(from区域)、s1区(to区域),From和To是两块大小相等,可以互换角色的区域。一般来说,新生的对象会被首先分配在Eden区,然经过一次GC之后(如果对象还存活)会到from或者to区。之后类似的每一次回收,都会加1,当对象达到一定年龄后,会进入老年代。
2、栈:Java栈是一个线程私有的空间,一般情况下一个栈由3部分组成:局部变量表、操作数栈、帧数据区。
局部变量表:里面存放的是报错函数的参数以及局部变量;
操作数栈:其中保存计算过程中的中间结果,同时作为计算过程中变量的临时存储空间;
帧数据区:除了局部变量表和操作数栈之外,还需要一些数据来支持常量池的解析,帧数据区中保存着访问常量池的指针,方便程序访问常量池。除此之外,当函数返回或者出现异常时,JVM必须有一个异常处理表,方便发送异常的时候找到异常的代码(从而异常处理也是帧数据区的一部分)
3、方法区:方法区是一块所有线程共享的内存区域,他保存了类的信息(类的字段、方法、常量池等等),方法区大小决定了系统可以保存多少个类。
4、一张简略的图描述一下堆、栈、方法区之间的关系
三、探秘JVM堆中对象分配
1、对象的创建
a)我们从new开始,当虚拟机遇到一条new指令的时候,会首先检查这个指令的参数能否在常量池中找到这个类的符号引用,并且检查这个类的加载是否被加载、解析、初始化过,如果没有就会按照类加载过程进行相应类的加载。
b)在类加载完毕后,然后JVM对新生对象分配内存,对象的分配简单而言就是在将一块确定大小的内存从Java堆中分配出来。
①堆是完整的(这时候所有使用的内存在一边,没有使用的内存在一边,中间放着一个指针作为分界点的指示器):分配内存就只是把指针向空闲内存那边移动与对象大小一样大的距离(这种方式成为“指针碰撞”)
②堆不是完整的(使用和未用的相互交错):虚拟机需要维护一个列表,其中记录的是空间内存的状况,在分配的时候从空闲内存中找到一块足够的空间划分给对象实例,然后更新表中的记录信息(这种方式成为“空闲列表”)
c)考虑多线程情况下的对象内存分配
在并发的情况下是线程不安全的,可能出现正在给对象A进行分配,这时候指针位置还没有来得及改变,然后这时候对象B的内存分配又使用了原来的指针记性分配。在《深入理解Java虚拟机》中讲到两种解决方案
①对对象的分配进行同步处理,采用CAS配置失败重试的方式保证更新操作的原子性
②将内存分配的动作按照线程划分在不同的空间中进行。即保证每个线程预先在Java堆中分配一小块空间(本地线程分配缓冲TLAB)。线程需要分配的时候,就先在TLAB上面分配,然后当TLAB使用完毕再进行同步锁定。
d)内存分配完成之后,虚拟机需要将分配的内存初始化为零值,这一步保证了对象实例字段在Java代码中可以不被赋初值就使用,使得程序能够访问到这些字段的数据类型对应的零值。
e)然后虚拟机需要对对象进行必要的设置,比如对象是那个类的实例,类的元数据、对象哈希码、GC年龄等等存放在对象头中。
f) 上面执行完毕之后,从虚拟机角度而言已经产生了心得对象。但是程序中对象创建还没有执行<init>方法,所有字段均为零值。所以,执行完new指令,还需要执行<init>方法,按照程序的角度进行初始化,才能使用这个对象。
2、对象的访问定位
我们在介绍堆栈的时候就介绍过堆栈和方法区之间的关系,Java程序中对象的引用存放在栈(reference)中,引用的实例存放在Java堆中(使用栈上面的对象引用来操作堆上面的具体对象),但是我们并没有定义怎样通过引用去定位、访问堆中的具体对象位置,下面介绍句柄和直接指针的方式
a)句柄方式
首先在Java堆中分配一块区域作为句柄池,栈中的reference中存放的就是对象的句柄地址信息(句柄中包含的是对象实例数据和类型数据各自的具体地址信息)。使用句柄方式的好处就是reference中存储的是稳定的句柄地址信息,而reference本身不需要修改。下面是句柄方式的简略图
b)直接指针方式:
Java堆中对象的布局中必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象的地址。使用直接指针的方式就是存取速度快,节省了一次指针定位的时间。
JVM自动内存管理机制——Java内存区域(上)的更多相关文章
- JVM自动内存管理机制——Java内存区域(下)
一.虚拟机参数配置 在上一篇<Java自动内存管理机制——Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...
- 2.1 自动内存管理机制--Java内存区域与内存溢出异常
自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存 ...
- Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制
Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制 http://w ...
- Java内存管理:Java内存区域 JVM运行时数据区
转自:https://blog.csdn.net/tjiyu/article/details/53915869 下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些, ...
- Java的内存管理机制之内存区域划分
各位,好久不见.先做个预告,由于最近主要在做Java服务端开发,最近一段时间会更新Java服务端开发相关的一些知识,包括但不限于一些读书笔记.框架的学习笔记.和最近一段时间的思考和沉淀.先从Java虚 ...
- Java虚拟机内存管理机制
自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区 ...
- 2、COCOS2D-X内存管理机制
在C++中.动态内存分配是一把双刃剑,一方面,直接訪问内存地址提高了应用程序的性能,与使用内存的灵活性.还有一方面.因为程序没有正确地分配与释放造成的比如野指针,反复释放,内存泄漏等问题又严重影响着应 ...
- 分布式缓存系统 Memcached 内存管理机制
在前面slab数据存储部分分析了Memecached中记录数据的具体存储机制,从中可以看到所采用的内存管理机制——slab内存管理,这也正是linux所采用的内存高效管理机制,对于Memchached ...
- Python内存管理机制-《源码解析》
Python内存管理机制 Python 内存管理分层架构 /* An object allocator for Python. Here is an introduction to the layer ...
随机推荐
- vue 全局组件【原】
1.目录 2.内容 -Loading.vue <template> <div class="loading"> loading... </div> ...
- Android 阻隔返回键
//阻隔 返回键 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.K ...
- SpringCloud-day02-服务消费者项目建立
4.4microservice-ticket-consumer-80服务消费者项目建立 我们新建一个服务器提供者module子模块,类似前面建的common公共模块,名称是 microservice- ...
- 循环TRUNCATE表,再ENABLE约束索引等
CREATE OR REPLACE PROCEDURE STG.FP_REMOVE_MST_OLD_DATA (EXITCODE OUT NUMBER) IS /******************* ...
- django 数据库配置 ,APP 迁移.模型基础
# 1.数据库的连接配置django 连接mysql的配置流程:- 安装 pymysql pip install pymysql- 创建数据库用户有创建数据库权限的用户- 创建数据库crm .进入数据 ...
- 360浏览器对CSS的补齐
360浏览器对很多CSS不兼容,导致了很多代码显示不正常, 常见的解决方法: 很多人在源代码加了<meta content=\"IE=edge\" http-equiv=\& ...
- Flask最强攻略 - 跟DragonFire学Flask - 第二篇 Flask 中的 Render Redirect HttpResponse
1.Flask中的HTTPResponse 在Flask 中的HttpResponse 在我们看来其实就是直接返回字符串 2.Flask中的Redirect 每当访问"/redi" ...
- python argparse(参数解析)模块学习(二)
转载自:http://www.cnblogs.com/fireflow/p/4841389.html(我去..没转载功能,ctrl + c 和 ctrl + v 得来的,格式有点问题,可去原版看看) ...
- 【计算机网络】TCP的流量控制和拥塞控制
TCP的流量控制 1. 利用滑动窗口实现流量控制 如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失.所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收. 利用滑动 ...
- 114. Flatten Binary Tree to Linked List 把二叉树变成链表
[抄题]: Given a binary tree, flatten it to a linked list in-place. For example, given the following tr ...