JVM内存的那些事
前言
对于C语言开发的程序员来说,在内存管理方面,必须负责每一个对象的生命周期,从有到无。
对于Java程序员你来说,在虚拟机内存管理的帮助下,不需要为每个new对象都匹配free操作,内存泄露和内存溢出等问题也不太容易出现,不过也正是因为把内存管理交给了虚拟机,一旦运行中的程序出现了内存泄露问题,给排查过程造成很大困难。所以只有理解了Java虚拟机的运行机制,才能够运筹帷幄于各种代码。本文以HotSpot为例说说虚拟机的那些事。
JAVA虚拟机把管理的内存划分为几个不同的数据区。
Java堆
Java堆是被所有线程共享的一块内存区域,主要用于存放对象实例,Java虚拟机规范中有这样一段描述:所有的对象实例和数据都要在堆上进行分配。为对象分配内存就是把一块大小确定的内存从堆内存中划分出来,通常有两种方法实现:
1 、指针碰撞法
假设Java堆中内存时完整的,已分配的内存和空闲内存分别在不同的一侧,通过一个指针作为分界点,需要分配内存时,仅仅需要把指针往空闲的一端移动与对象大小相等的距离。
2、空闲列表法
事实上,Java堆的内存并不是完整的,已分配的内存和空闲内存相互交错,JVM通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。
对象创建是一个非常频繁的行为,进行堆内存分配时还需要考虑多线程并发问题,可能出现正在给对象A分配内存,指针或记录还未更新,对象B又同时分配到原来的内存,解决这个问题有两种方案:
1、采用CAS保证数据更新操作的原子性;
2、把内存分配的行为按照线程进行划分,在不同的空间中进行,每个线程在Java堆中预先分配一个内存块,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB);
Java栈
Java栈是线程私有的,每个线程对应一个Java栈,每个线程在执行一个方法时会创建一个对应的栈帧(Stack Frame),栈帧负责存储局部变量变量表、操作数栈、动态链接和方法返回地址等信息。每个方法的调用过程,相当于栈帧在Java栈的入栈和出栈过程。
局部变量表 用于存放方法参数和方法内部定义的局部变量,其大小在代码编译期间已经确定,在方法运行期间不会改变。局部变量表以变量槽(Slot)为最小存储单位,每个Slot能够存放一个boolean、byte、char、shot、int、float、reference和returnAddress类型的32位数据,对于64位的数据类型long和double,虚拟机会以高位对齐的方式为其分配两个连续的Slot空间。
在方法执行时,如果是实例方法,即非static方法,局部变量表中第0位Slot默认存放对象实例的引用,在方法中可以通过关键字 this 进行访问,方法参数按照参数列表顺序,从第1位Slot开始分配,方法内部变量则按照定义顺序进行分配其余的Slot。
class test { public int calc(int a, int b, String operation) { operation = "+"; return a + b; } public void main(String args[]) { calc(100, 200, "+"); } }
对应的局部变量表如下:
使用 javap -c 命令查看方法calc的字节码
其中iload_1和iload_2分别从局部变量表中的第1位和第2位中加载数据。
方法区
方法区和Java堆一样,是所有线程共享的内存区域,用于存放已被虚拟机加载的类信息、常量、静态变量和即时编译器编译后的代码等数据。
运行时常量池是方法区的一部分,用于存放编译期间生成的各种字面常量和符号引用。
指令计数器
指令计数器是线程私有的,每个线程都有独立的指令计数器,计数器记录着虚拟机正在执行的字节码指令的地址,分支、循环、跳转、异常处理和线程恢复等操作都依赖这个计数器完成。如果线程执行的是native方法,这个计数器则为空。
对象的内存布局
对象在内存中布局可以分成三块区域:对象头、实例数据和对齐填充。
1、对象头
对象头包括两部分信息:运行时数据和类型指针,如果对象是一个数组,还需要一块用于记录数组长度的数据。
1.1、运行时数据包括哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向锁ID和偏向时间戳等,这部分数据在32位和64位虚拟机中的长度分别为32bit和64bit,官方称为”Mark Word”。Mark Word被设计成非固定的数据结构,以实现在有限空间内保存尽可能多的数据。
32位的虚拟机中,对象未被锁定的状态下,Mark Word的32bit中25bit存储对象的HashCode、4bit存储对象分代年龄、2bit存储锁标志位、1bit固定为0,具体如下:
其它状态(轻量级锁定、重量级锁定、GC锁定、可偏向锁)下Mark Word的存储内容如下:
1.2、对象头的类型指针指向该对象的类元数据,虚拟机通过这个指针可以确定该对象是哪个类的实例。
2、实例数据
实例数据就是在程序代码中所定义的各种类型的字段,包括从父类继承的,这部分的存储顺序会受到虚拟机分配策略和字段在源码中定义顺序的影响。
3、对齐填充
由于HotSpot的自动内存管理要求对象的起始地址必须是8字节的整数倍,即对象的大小必须是8字节的整数倍,对象头的数据正好是8的整数倍,所以当实例数据不够8字节整数倍时,需要通过对齐填充进行补全。
JVM内存的那些事的更多相关文章
- JVM 内存的那些事
转自:http://blog.jobbole.com/104863/ 对于Java程序员你来说,在虚拟机内存管理的帮助下,不需要为每个new对象都匹配free操作,内存泄露和内存溢出等问题也不太容易出 ...
- JVM内存划分基础知识
第一部分 JVM内存划分 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbag ...
- Inside JVM 内存模型
Inside JVM 内存模型 来源 原文:https://blog.csdn.net/silentbalanceyh/article/details/4661230 参考:IBM开发中心文档,&l ...
- JVM内存GC的骗局——JVM不抛出OOM但内存已经泄露
概述 在日常测试中,我们会去重点观察java的内存使用情况,比如:进程会抛出OOM异常,不再接收新的请求:响应时间在固定时间段内变长,超时或者不响应,CPU使用率时常像过山车一样等.有时候JVM还会发 ...
- JVM内存管理及GC机制
一.概述 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露 ...
- 第八章 JVM内存管理
8.1 物理内存与虚拟内存 地址总线(连接处理器和RAM或处理器和寄存器的)的宽度影响了物理地址的索引范围,决定了处理器一次可以从寄存器或内存中获取多少个bit.同时决定了处理器最大的寻址空间,32位 ...
- JVM内存模型 三
本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉 ...
- JVM内存GC的骗局
此文已由作者尧飘海授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 概述 在日常程序开发中,很多JAVA程度员不太关心内存的使用情况.当然,如果程序员运气较好或者系统没有大规模 ...
- 谈谈JVM内存区域的划分
我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等 ...
随机推荐
- c# -- 读取文件夹中的所有文件(备忘)
读取选取的文件夾下的所有.txt文件 private void button1_Click(object sender, EventArgs e){ if (folderBrowserDialog1. ...
- Sass中的mixin,function,extend
Mixins: 用于相类似的css属性将会被使用多次,每次调用时仅仅有小的参数改变: Function 用于计算得出相关值: Extend 有一批属性完全匹配时,应该使用extend
- core--进程
前面我们说了线程是一系列的指令,那么进程是什么呢?进程就是装下这些指令的容器.该容器除了线程,还包含资源等内容.一个进程至少要有一个线程.没有线程的进程就不叫进程 "进程和程序的区别:进程必 ...
- UVa 537 Artificial Intelligence?
题目大意:输入一个字符串,根据物理公式P=U*I,已知其中两个量,求第三个量,结果保留两位小数. Artificial Intelligence? Physics teachers in hig ...
- (转载)DataTable使用技巧总结
在项目中经常用到DataTable,如果DataTable使用得当,不仅能使程序简洁实用,而且能够提高性能,达到事半功倍的效果,现对DataTable的使用技巧进行一下总结. 一.Da ...
- 在asp.net前台页面中引入命名空间 和连接数据库
例如:<%@ Import Namespace="System.Data" %> 连接数据库 <% string strconn = "Data Sou ...
- LeetCode Count Primes 求素数个数(埃拉托色尼筛选法)
题意:给一个数n,返回小于n的素数个数. 思路:设数字 k =from 2 to sqrt(n),那么对于每个k,从k2开始,在[2,n)范围内只要是k的倍数的都删掉(也就是说[k,k2)是不用理的, ...
- iOS 9的新内容
https://www.hackingwithswift.com/ios9 Search extensibility Update: I wrote a tutorial on Core Spotli ...
- JDK,JRE,JVM区别与联系-理解与概括
我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析 ...
- C++ 编写Windows service
最近实现一个windows server端守护进程启动服务功能(c++实现),遇到了一些问题,记录一下 1. 启动Service实现代码: int _tmain(int argc, TCHAR* ar ...