Java内存区域 对象 读笔

深入理解Java虚拟机这本书,JVM的内存管理,GC,JVM加载等内容有了一个较清晰的理解;但是对于Java字节码执行,内部优化的一些内容还是感觉比较吃力,这些内容还需要再看,再消化..

Java内存区域

运行时数据区域:
JVM在执行Java程序过程中将其管理的内存划分为若干个不同的数据区域。每个区域都有其各自的用途,以及创建和销毁的时间。

这里写图片描述

  • 程序计数器 (Program Counter Register):
    是一块较小的内存区域,是当前线程所执行的字节码的行号指示器。在JVM概念模型中(实际中可能采用更加高效方式),字节码解释工作就是通过改变这个计数器的值来选择下一条需要执行的字节码指令。
    为了在线程切换后恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,每个线程之间的计数器相互独立。
    执行的是Java方法,记录的是正在执行的虚拟机字节码指令的地址;是Native方法,计数器则为空。
    该区域为唯一一个没有规定任何OutOfMemoryEror的区域。

  • Java虚拟机栈(Java Virual Machine Stacks):
    是Java方法执行的内存模型,线程私有,且生命周期与线程相同。
    每个方法在执行的同时将创建一个栈帧(Stack Frame),该栈帧存储着局部变量表,操作数栈,动态链接,方法出口等信息。每个方法调用至执行完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈。

    • 局部变量表:存储了编译器可知的基本数据类型,对象引用(reference类型)和returnAddress类型(指向一条字节码的地址)。
      局部变量表需要的内存空间在编译期间完成分配,每个方法需要在帧中分配的局部变量空间是完全确定的,且运行期间不会改变。

    异常:

    • StackOverflowError异常:线程请求的栈深度大于虚拟机允许的深度。(方法过多?)
    • OutOfMemoryError异常:当栈可以动态扩展,直至扩展时无法申请到足够的内存。
  • 本地方法栈:(Native Method Stack):
    作用于虚拟机栈作用相似,区别在于本地方法栈为Native方法服务,虚拟机栈为Java方法(字节码)服务。

  • Java堆(Java Heap):
    JVM管理的内存最大的一块,被所有线程所共享,作用是存放对象实例。
    Java堆是垃圾收集管理的主要区域,因此也称为“GC堆”。在后期会详细介绍内部的细分,回收方式。
    异常:OutOfMemory:当堆内内存不足完成实例分配,并且堆无法再扩展。

  • 方法区(Method Area):
    被所有线程共享,存储已被JVM加载的类信息,常量,静态变量,即时编译器编译后的代码等。
    垃圾收集行为在该区域较少出现,因该区域内的回收目标(常量池,类型的卸载)的回收条件苛刻,回收效果差。
    异常:OutOfMemory:当无法满足内存分配需求。

其他区域:

  • 运行时常量池(Runtime Constant Pool):
    属于方法区的一部分。用于存储Class文件中的常量池(Constant Pool Table),存放着编译期生成的各种字面量和符号引用(一个包含着一些足以唯一的识别一个类,方法,变量的字符串;会在装载class文件时转化为直接引用,直接引用也存储在这)。
    动态性:运行期间也可能将新的变量放入池中(如String类的intern方法,将某字符串置入常量池)。
    异常:OutOfMemory:当无法申请到内存时。

  • 直接内存(Direct Memory):
    不属于运行时数据区和JVM规定的内存区域。在NIO中,被一个存储在Java堆中的DirectByteBuffer对象作为引用而进行操作。
    不受到Java堆大小的限制,受到本机内存以及寻址空间限制。

对象解析

指普通的Java对象,不包括Class对象与数组等。

对象的创建过程:

  • 类加载检查
    虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。(没有,则执行类加载过程,在后文具体解释)

  • 为新生对象分配内存
    对象所需内存的大小在类加载完成后便可以完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。根据Java堆中内存是否规整(取决于GC算法?),划分的方法主要有两种:

    • 指针碰撞(Bump the Pointer):
      当内存为绝对规整,将存在一个用过内存和空闲内存的分界点指针,通过将指针向空闲内存一端移动所需要的大小。
    • 空闲列表(Free List):
      当内存不规整,则需要在已使用与空闲内存交错内存块上需要可用的内存,则需要通过维护一个记录着哪些内存块可用的列表;通过在列表上找到一块足够大的看空间块,并更新列表。

    因对象创建非常频繁,则需要考虑在并发条件下的分配内存的方案:

    • 对分配内存的动作进行同步处理:通过采用CAS和失败重试的方式保证更新操作原子性。
    • 对分配内存的动作按照线程划分在不同空间进行:本地线程分配缓冲,每个线程在Java堆中预先分配一块内存。
  • 后续工作

    • 初始化:将分配到的内存空间都初始化为零值(不包括对象头)。
    • 必要的设置:主要是对对象头内信息的设置。对象头内包含着对象的很多信息:该对象是哪个类的实例,如何找到类的元数据,对象的哈希值,对象的GC分代年龄等。

以上完成后,对于JVM一个对象已经产生了;但从Java程序角度,所有字段都还为默认值,需要方法,将对象按照需要初始化。

对象的内存布局:
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域: 对象头(Header),实例数据(Instance Data),对齐补充(Padding)。

  • 对象头
    对象头中包含两部分信息:Mark Word和类型指针。

    • MarkWord:用于存储对象自身的运行时数据。如哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等。
      MarkWord被设计为一个非固定的数据结构在极小的空间内存储尽可能多的信息。32位,64位虚拟机中大小分别为32bit,64bit。
      32位虚拟机对象头例子:
      这里写图片描述
    • 类型指针:对象指向它的类元数据的指针,JVM通过这个指针确定这个对象是哪个类的实例。
  • 实例数据:
    对象真正存储的有效信息,找程序代码中所定义的各种类型的字段内容。
    存储顺序受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响。

  • 对齐填充:
    不一定存在,只是起到占位符的作用。因为虚拟机规定对象起始位置必须是8字节的整数倍,即对象大小必须是8字节的整数倍,则当没有对齐时,通过对齐填充补齐。

对象的访问定位:
通过(虚拟机栈)栈上的reference数据操作(GC堆)堆上的具体对象。reference类型在Java虚拟机规范中只规定为指向一个对象的引用,没有规定实现方式等等…则取决于虚拟机实现。
实现方式:

  • 句柄访问:
    在Java堆中划分出一块内存作为句柄池,reference存储的是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
    好处:reference存储的是稳定的句柄地址。在对象被移动(GC时可能会移动对象)只会改变句柄的实例数据指针,对reference没有影响。
    这里写图片描述
  • 直接指针:
    堆对象中需要考虑怎么放置访问类型数据相关信息,reference中存储的直接就是对象地址。
    好处:速度快,节省了一次指针定位的时间开销(对象实例数据指针的定位过程)。
    这里写图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目:使用 JavaScript 编写的杀死幽灵游戏(附源代码) 杀死鬼魂游戏是使用 Vanilla JavaScript、CSS 和 HTML 画布开发的简单项目。这款游戏很有趣。玩家必须触摸/杀死游荡的鬼魂才能得分。您必须将鼠标悬停在鬼魂上 - 尽量得分。鬼魂在眨眼间不断从一个地方移动到另一个地方。您必须在 1 分钟内尽可能多地杀死鬼魂。 游戏制作 这个游戏项目只是用 HTML 画布、CSS 和 JavaScript 编写的。说到这个游戏的特点,用户必须触摸/杀死游荡的幽灵才能得分。游戏会根据你杀死的幽灵数量来记录你的总分。你必须将鼠标悬停在幽灵上——尽量得分。你必须在 1 分钟内尽可能多地杀死幽灵。游戏还会显示最高排名分数,如果你成功击败它,该分数会在游戏结束屏幕上更新。 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox。要玩游戏,首先,单击 index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
javascript 中的 Paint War Game 是使用 HTML、CSS 和 JavaScript 开发的。谈到游戏玩法,这款游戏的主要目标是建造比敌人更多的油漆砖。您所要做的就是使用 WASD 键输入玩家的动作。您可以使用 VS Code 来运行该项目。 关于项目 每次您的玩家走过一块瓷砖时,它都会被涂成您的团队颜色。您必须在同一块瓷砖上走 4 次才能获得更多游戏点数。瓷砖会被您的团队挡住,并且不能再被偷走。如果您走过另一支球队的瓷砖,它会像您第一次走过时一样被涂上颜色。如果您创建一个封闭的被阻挡瓷砖图形,图形内所有未被阻挡的瓷砖都将固定为您的团队颜色。这个游戏充满乐趣,创造和重新即兴发挥会更有趣。 要运行此项目,我们建议您使用现代浏览器,例如 Google Chrome、  Mozilla Firefox。该游戏可能还支持 Explorer/Microsoft Edge。 演示: javascript 中的 Paint War Game 是使用 HTML、CSS 和 JavaScript 开发的。谈到游戏玩法,这款游戏的主要目标是建造比敌人更多的油漆砖。您所要做的就是使用 WASD 键输入玩家的动作。您可以使用 VS Code 来运行该项目。 关于项目 每次您的玩家走过一块瓷砖时,它都会被涂成您的团队颜色。您必须在同一块瓷砖上走 4 次才能获得更多游戏点数。瓷砖会被您的团队挡住,并且不能再被偷走。如果您走过另一支球队的瓷砖,它会像您第一次走过时一样被涂上颜色。如果您创建一个封闭的被阻挡瓷砖图形,图形内所有未被阻挡的瓷砖都将固定为您的团队颜色。这个游戏充满乐趣,创造和重新即兴发挥会更有趣。 要运行此项目,我们建议您使用现代浏览器,例如 Google Chrome、  Mozilla Firefox。该游戏可能还支持 Explorer/Microsoft Edge。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值