前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JVM学习笔记——Java内存区域与内存溢出异常

JVM学习笔记——Java内存区域与内存溢出异常

作者头像
用户1665735
发布2019-02-19 12:39:15
6300
发布2019-02-19 12:39:15
举报
文章被收录于专栏:kevindroidkevindroid

运行时数据区域

这里写图片描述
这里写图片描述

其中,其中Method Area 和 Heap 是线程共享的 ,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享的。JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。

程序计数器

程序计数器是一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。为了线程切换后能够恢复到正确的位置。 如果线程执行java方法,计数器记录正在执行的虚拟机字节码指令的地址,如果是native方法,值为空。 简单地讲,一个Native Method就是一个java调用非java代码的接口。

Java虚拟机栈

描述的是一个java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量,操作数栈,动态链接,方法出口等信息。 这个区域有两种异常情况:线程请求的栈深度>JVM允许的深度,导致stackoverflow错误; 扩展时无法获取足够的内存,导致OutOfMemoryError错误。

本地方法栈

与java虚拟机栈类似,不同的是存储的是本地方法。

java堆

线程共享的内存区域,在虚拟机启动时创建,用来存放对象实例。 java堆是垃圾收集器管理的主要区域,由于垃圾收集器都采用分代收集算法,一般分为新生代与老年代,再细致一点分为Eden空间,From Survivor空间,To Survivor空间。 java堆可以处于物理上不连续但逻辑上连续的空间。

方法区

线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。又被称为“永久代”,因为GC分代扩展到了方法区,或者说使用永久代来实现方法区而已。 和java堆一样,只需要逻辑上连续的空间。还可以选择不实现垃圾收集,因为这个区域的内存回收目标主要针对常量池的回收以及类型的卸载,但是类型的卸载条件相当严格,所以回收效率不高。

运行时常量池

方法区的一部分,存放编译器生成的各种字面量和符号引用。JVM规范并未对这部分做严格要求,所以提供商可以按照自己的要求实现这部分。运行时常量池具有动态性,运行期的常量也可以放入池中,如String类的intern()方法。

直接内存

这部分并不是虚拟机运行数据区的一部分,也不是JVM规范中定义的内存区域。虽然本机直接内存的分配不会受到java堆的影响,但是还会受到本机总内存以及处理器寻址空间的限制。

HotSpot对象

对象的创建

这里写图片描述
这里写图片描述

指针碰撞:假设java堆中内存是绝对规整的,中间放着一个指针作为分界点的指示器,分配内存只需把指针向空闲空间那边移动一段与对象大小相等的距离。 空闲列表:内存不规整,虚拟机必须维护一个表,记录哪些内存块是可用的,从中找到一块足够大的内存分配给实例。 对象创建时的冲突:创建对象是一个非常频繁的行为,并发情况下修改指针位置并不是线程安全的,可能出现给A对象分配内存是指针还未来得及修改B对象就是用这个指针进行下一步操作。有两种解决方案:

  • 对分配内存空间的动作进行同步处理
  • 把内存分配的动作按照线程划分到不同的空间中进行,即每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)

对象的内存布局

对象中的内存布局可以分为三个区域:对象头(Header),实例数据(Instance Data),对齐填充(Padding)。

对象头

对象头包括两部分信息,一部分用于存储对象自身的运行时数据,如哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等,被称为“Mark Word”。在32位与64位的虚拟机中分别位32bit以及64bit。在32bit状态下,25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0,其他情况下存储状况如下:

存储内容

标志位

状态

对象的哈希码,分代年龄

01

未锁定

指向锁记录的指针

00

轻量级锁定

指向重量级锁的指针

10

膨胀(重量级锁定)

空,不需要记录信息

11

GC标记

偏向线程ID,偏向时间戳,对象分代年龄

01

可偏向

对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针判断这个对象是哪一个类的实例。并不是所有虚拟机都必须这一部分。

实例数据

对象真正存储的有效信息,即程序代码中定义的各种类型的字段内容,无论是父类继承的还是子类定义的都需要记录。存储顺序受虚拟机分配参数以及字段在java源码中的定义顺序的影响。

对齐填充

不是必须存在的,因为虚拟机的内存管理系统要求对象的起始地址必须是8字节的整数倍,即对象的大小必须是8字节的整数倍,当对象实例数据部分没有对齐时,需要通过对齐填充来补全。

对象的访问定位

java程序通过栈上的reference数据来操作堆上具体的对象,但是虚拟机并没有定义该通过何种方式区定位,访问堆中的对象的具体位置。目前的主流访问方式有使用句柄和直接访问两种。

句柄访问

这里写图片描述
这里写图片描述

直接指针访问

句柄访问的优势:reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,reference本身不需要修改。 直接指针访问的优点:速度快,节省了一次指针定位的时间开销。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年03月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 运行时数据区域
    • 程序计数器
      • Java虚拟机栈
        • 本地方法栈
          • java堆
            • 方法区
              • 运行时常量池
                • 直接内存
                • HotSpot对象
                  • 对象的创建
                    • 对象的内存布局
                      • 对象头
                      • 实例数据
                      • 对齐填充
                    • 对象的访问定位
                      • 句柄访问
                      • 直接指针访问
                  相关产品与服务
                  对象存储
                  对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档