JVM 基础 1

一、Java内存区域与内存区域溢出异常
1. 程序计数器(program Counter Register):一个较小的内存空间
作用:通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能
Java虚拟机的多线程是通过线程轮流切换并分配处理器的方式实现,任何一个确定的时刻,一个处理器(多核处理器为一个内核)只会执行一条线程的指令
线程间的独立计数器,称之为线程私有的内存。
2. Java虚拟机栈(Java Virtual Machine Stacks)--Java方法执行的内存模型
线程私有的 生命周期与线程相同
在每个方法执行的同时都会创建一个和栈帧(Stack Frame)用于存储局部变量、操作数栈、动态链接、方法出口。

简单描述Java内存区:堆内存(heap),栈内存(Stack)

局部变量表存储了编译期可知的各种 基本数据类型(Boolean、byte、char、short 、int、float、long、double){其中64位长度}、对象引用(reference 类型)、和returnAddress类型(指向了一条字节码指令的地址)

3 .Java堆(heap):管理内存 最大一块,是所有线程共享的内存区域
用于存放对象实例---所有对象实例以及数组都要在堆上分配
Java堆也被称为GC堆,是垃圾收集与管理的主要区域。
粗分为:新生代,老年代
4. 方法区(Method Area) --各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
也被称之为 Non-Heap(非堆)
也称 永久代(Permanent Generation)。




5. 运行时常量池(属于方法区的一部分)
Class文件中除了有类的版本、字段、方法、接口等描述信息之外,外加常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号的引用,这部分将在类加载之后进入方法区的运行时常量池中存放。(把翻译出来的直接引用也存储在运行时常量池中)——————运行期间也会有新的常量放入常量池中,如 String类intern()类。

6.直接内存(Direct Memory)



2).对象的创建
虚拟机遇上new指令,首先检查指令的参数是否能够在常量池中定位到一个类的 符号引用 ,并检查这个符号引用代表的类是否已经被 加载、解析、 初始化过 。若有则先执行相应的类的加载过程 。类的加载通过之后,虚拟机为新生对象分配内存,对象所需要的大小在类加载完可完全确定(。。。)。
指针碰撞式分配方式(Bump the Pointer) :如若Java内存绝对规整,分配内存则是将指针向着空闲空间那边挪动一个与对象大小相等的距离; 如Serial,ParNew等带compact过程的收集器。
空闲列表分配方式(Free List): Java内存不规整,已使用内存与空闲内存交错,虚拟机需要维护一个列表,记录空闲区域,分配时从列表上找到一块足够大的空间划分给对象实例,并且更新列表的记录; 如CMS这种基于Mark—Sweep算法的收集器。

分配内存的考虑:指针未完全释放又被使用,抢占——解决方案:
对分配内存空间的动作进行同步处理---实际上虚拟机采用CAS配上 失败重试 的方法保证更新操作的原子性。(线程不安全的解决方案)
把内存的分配动作按照线程划分在不同的空间中进行,即每个线程在Java堆中预先分配一小块内存,称之为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)只有在TLAB使用完了之后并且分配了新的TLAB时,才需要同步锁定。

此时虚拟机将对对象进行设置:属于此对象属于哪个实例、如何才能找到类的元素信息、对象的哈希表、对象的GC分代年龄等信息。。。这些信息存放于对象的 对象头(Object Header)

虚拟机——>新对象产生
Java程序——>对象创建刚开始 <init>方法还未执行,所有字段都为零

7.对象的内存布局
对象在虚拟机中的存储布局可分为三块区域: 对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

对象头:
①运行时数据:哈希码,GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
②类型指针:
③如果为数组,还需记录数组长度的数据(虚拟机可通过Java对象元素数据确定Java对象的大小,但是从数组的元数据却无法确认数组的大小)

实例数据:是真正存储的有效信息,代码中定义的字段及内容
相同宽度的字段都被分配到一起,,满足这个前提下,父类的定义变量会出现在子类之前,CompactFileds参数值为true,所以子类中较窄的变量也会插入父类变量的空隙之中。


对齐填充:占位符作用 (HotSpot VM的自动内存管理系统对象必须是8字节的整数倍,换句话说,对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1或者2倍)。)




对象的访问定位:
建立访问对象
访问方式: 句柄直接指针

句柄访问: Java堆中划分出一块内存作为句柄池,reference(变量)中存储的就是对象的句柄地址,而句柄中包含对象实例数据和类型数据各自的具体地址。

直接指针 :直接指针使得Java堆对象的布局中就必须考虑如何放置访问类型数据。而reference存的是直接就是对象地址。
对比: 前者就好比你知道一个人电话号码是什么,无论他在搬家到哪,你都能找到。
好处是reference存储的是稳定的句柄地址,对象被移动时(垃圾收集时候很常见)只会改变句柄中的实例数据指针,reference不需要修改。
而后者就好比直接指针就是知道你住哪栋哪单元,直接告诉你什么事情。
好处是速度快,,节省了指针定位的时间。sun hotspot 属于这种。


········································


Java堆溢出

虚拟机栈和本地方法栈溢出

方法区和运行时常量池溢出




jdk1.7逐步“去永久代”

String。intern()作用:若字符串常量池中已经包含一个等于此String对象的字符串,则返回代表常量池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回String对象的引用。


jdk1.6,intern()方法会把首次遇到的字符串复制到永久代中,返回是永久代中这个字符串实例的引用。而jdk1.7中的intern()实现不会再复制实例,而是会在常量池中记录首次出现的实例引用。

本机直接内存溢出





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值