java内存分配研究

合集下载

java内存结构

java内存结构

java内存结构Java的内存结构JVM的内存结构主要有三⼤块:堆、⽅法区和栈。

堆内存是JVM中最⼤的⼀块,由年轻代和⽼年代组成,⽽年轻代内存⼜被分为三部分,Eden空间、FromSurvivor空间和ToSurvivor空间,默认情况下年轻代是按照8:1:1的⽐例来分配。

⽅法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,⽅法区还有⼀个别名Non-Heap(⾮堆);栈⼜分为Java虚拟机栈和本地⽅法栈主要⽤于⽅法的执⾏。

JVM和系统调⽤之间的关系⽅法区和堆是所有线程共享的内存区域;⽽java虚拟机栈、本地⽅法栈和程序员计数器是线程私有的内存区域。

1. Java堆(Heap)对于⼤多数应⽤来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最⼤的⼀块,Java堆是被所有线程共享的⼀块内存区域,在虚拟机启动时创建。

此内存区域的唯⼀⽬的就是存放对象实例,⼏乎所有的对象实例都在这⾥分配内存。

Java堆是垃圾收集器管理的主要区域,因此很多时候也被成为“GC堆”。

如果从内存回收的⾓度看,由于现在收集器基本都是采⽤的分代收集算法,所以Java堆中还可以细分为:新⽣代和⽼年代,再细致⼀点的有Eden空间、From Survivor空间、ToSurvivor空间等。

根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的空间内存中,只要逻辑上是连续的即可,就像我们的磁盘空间⼀样。

在实现时,既可以实现成固定⼤⼩的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。

如果在堆中没有内存完成实例分配,并且堆也⽆法再扩展时,将会抛出OOM(OutOfMemoryError)异常。

2. ⽅法区(Method Area)⽅法区与Java堆⼀样,是各个线程共享的内存区域,它⽤于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,虽然Java虚拟机规范把⽅法区描述为堆的⼀个逻辑部分,但是它有⼀个别名Non-Heap(⾮堆),⽬的应该是与Java堆区分开。

Java虚拟机内存分配探析

Java虚拟机内存分配探析

量分 配 内存空 间 ,当超 过变 量 的作用 域后 , v 会 自动 释放 掉 J a a
为 该 变 量 所 分 配 的 内 存 空 间 ,该 内 存 空 间 可 以 立 即被 另 作 他
用。
享 与 两个 对象 的 引用 同时指 向一 个对 象 的这种 共 享是 不 同的 ,
因 为 这 种 情 况 a的 修 改 并 不 会 影 响 到 b .它 是 由 编 译 器 完 成 的 , 有 利 于 节 省 空 间 。 而 一 个 对 象 引 用 变 量 修 改 了 这 个 对 象 它 的 内部状 态 , 影 响 到另 一个 对象 引用 变量 。 会
间 。对象 通过 nw、e ary等 方式 建立 , 需要 程 序代 码来 显 e nw r a 不
第9 第7 卷 期
2 1 年 7月 00
软 件 导 刊
So t r i e fwa e Gu d
VO . 1 NHale Waihona Puke . 9 7 J . 01 u12 O
Jv a a虚 拟 机 内存 分 配 探 析
胡 雯
( 汉科 技 大 学 中南分校 , 北 武 汉 4 02 ) 武 湖 3 2 3
2 S r g 对 象 举 例 说 明 ti 类 n
Sr g是一 个特 殊 的包装 类 数据 。可 以用 : tn i
Sr gs = e t n (a c ) tn t nw Sr g ”b ” ; i r - i
S r g sr a c ; t n t i =” b ”
是 为 数 组 或 对 象 起 的 一 个 名 称 , 后 就 可 以 在 程 序 中 使 用 栈 中 以 的引用变 量来 访 问堆 中的数 组 或对 象 。 Jv aa是 自动 管 理 栈 和 堆 , 序 员 不 能 直 接 地 设 置 栈 或 堆 。 程 Jv aa的堆 是 一 个 运 行 时 数 据 存 储 区 , 的 对 象 从 中 分 配 空 类

JAVA内存管理模式

JAVA内存管理模式
【参考文献】 1. 樊瑞君. 从评估工作看加强高职院校档案管理工作的必要 性[J]. 中国教育与社会科学,2009 2. 王峰. 从高校教学评估看档案管理工作的重要性[J]. 中国 市场,2009 Nhomakorabea·248·
Industrial & Science Tribune 2011.(10).12
产业与科技论坛 2011 年第 10 卷第 12 期
( 四) 提高对档案管理工作的认识,增强档案意识。为了 更好地发挥档案工作的重要作用,高校档案管理部门应广泛 开展档案法规、意识、宣传活动,让全体教职员工更多地了解 档案、关心档案,提高对档案工作重要性的认识,调动他们形 成档案、保护档案 的 自 觉 性,加 强 档 案 管 理 工 作 的 服 务 意 识 要从根本上改变过去“重保管、轻利用”的现象,转变观念,把 工作的重点放在 档 案 资 源 的 开 发 与 利 用 上 ,搞 活 档 案 工 作。 使档案工作与学校工作同步开展。
【关键词】Java,堆内存; 栈内存; 静态域; 常量池; 内存分配 【作者单位】秦靖伟,吉林工商学院信息工程分院
▲ ▲
一、引言 JAVA 自上个世纪 90 年代初期诞生以来,发展到今天已 经被业界广泛的认可,其为编程( 尤其是网络编程) 方面所带 来的巨大变革,是其他语言所不可比拟的。它以自身的纯粹 的面向对象,分布 式,安 全 性,健 壮 性,与 平 台 无 关 性 等 特 点 正在被全世界的程序员所推崇。但伴随而来的也有一些质 疑,比如 JAVA 在编制桌面程序,以及在程序的执行效率方面 确实还有差强人意的地方,其原因何在? 本文试就上述问 题,从内存的角度分析 JAVA 的内部机制,以使读者更深入地 了解和掌握 JAVA 语言。 二、Java 的内存分配策略 JAVA 程序在运行时,不同平台上的 JVM 都会提供如下 图所示的运行时数据区组件:

java内存使用情况的命令

java内存使用情况的命令

java内存使用情况的命令Java是一种面向对象的编程语言,它在开发应用程序时需要使用内存来存储数据和执行代码。

因此,了解Java的内存使用情况对于开发人员来说是非常重要的。

Java虚拟机(JVM)负责管理Java应用程序的内存,它使用垃圾回收机制来自动管理内存的分配和释放。

JVM的内存可以分为以下几个部分:1. 堆(Heap):堆是Java程序运行时动态分配的内存区域,用于存储对象实例。

堆的大小可以通过命令行参数-Xmx和-Xms来设置。

-Xms表示JVM启动时初始分配的堆内存大小,-Xmx表示堆能够达到的最大内存大小。

2. 方法区(Method Area):方法区用于存储已加载的类信息、常量、静态变量等数据。

方法区的大小可以通过命令行参数-XX:PermSize和-XX:MaxPermSize来设置。

-XX:PermSize表示JVM启动时初始分配的方法区大小,-XX:MaxPermSize表示方法区能够达到的最大大小。

3. 栈(Stack):栈用于存储Java方法中的局部变量以及方法调用时的状态信息。

每个Java线程都有一个独立的栈,栈的大小是固定的,并且在线程创建时被分配。

栈的大小可以通过命令行参数-Xss来设置。

除了上述部分,JVM还会使用一些额外的内存空间,如直接内存(DirectMemory)和本地方法栈(Native Method Stack),用于存储一些特殊的数据和执行本地方法。

了解Java的内存使用情况对于定位内存泄漏和优化程序性能非常有帮助。

下面是几个常用的命令,可以用于监控和调整Java程序的内存使用情况:1. jps:该命令用于列出当前运行的Java进程,以及对应的进程ID。

2. jstat:该命令用于监控Java虚拟机的各种运行状态,包括堆的使用情况、类加载数量、垃圾回收情况等。

常用的参数包括-jstat -gcutil <pid>和-jstat-gccapacity <pid>。

实现内存分配实验报告(3篇)

实现内存分配实验报告(3篇)

第1篇一、实验目的1. 理解操作系统内存分配的基本原理和常用算法。

2. 掌握动态分区分配方式中的数据结构和分配算法。

3. 通过编写程序,实现内存分配和回收功能。

二、实验环境1. 操作系统:Linux2. 编程语言:C语言3. 开发工具:GCC编译器三、实验原理1. 内存分配的基本原理操作系统内存分配是指操作系统根据程序运行需要,将物理内存分配给程序使用的过程。

内存分配算法主要包括以下几种:(1)首次适应算法(First Fit):从内存空间首部开始查找,找到第一个满足条件的空闲区域进行分配。

(2)最佳适应算法(Best Fit):在所有满足条件的空闲区域中,选择最小的空闲区域进行分配。

(3)最坏适应算法(Worst Fit):在所有满足条件的空闲区域中,选择最大的空闲区域进行分配。

2. 动态分区分配方式动态分区分配方式是指操作系统在程序运行过程中,根据需要动态地分配和回收内存空间。

动态分区分配方式包括以下几种:(1)固定分区分配:将内存划分为若干个固定大小的分区,程序运行时按需分配分区。

(2)可变分区分配:根据程序大小动态分配分区,分区大小可变。

(3)分页分配:将内存划分为若干个固定大小的页,程序运行时按需分配页。

四、实验内容1. 实现首次适应算法(1)创建空闲分区链表,记录空闲分区信息,包括分区起始地址、分区大小等。

(2)编写分配函数,实现首次适应算法,根据程序大小查找空闲分区,分配内存。

(3)编写回收函数,回收程序所占用的内存空间,更新空闲分区链表。

2. 实现最佳适应算法(1)创建空闲分区链表,记录空闲分区信息。

(2)编写分配函数,实现最佳适应算法,根据程序大小查找最佳空闲分区,分配内存。

(3)编写回收函数,回收程序所占用的内存空间,更新空闲分区链表。

3. 实验结果分析(1)通过实验,验证首次适应算法和最佳适应算法的正确性。

(2)对比两种算法在内存分配效率、外部碎片等方面的差异。

五、实验步骤1. 创建一个动态内存分配模拟程序,包括空闲分区链表、分配函数和回收函数。

Java中堆与栈的内存分配

Java中堆与栈的内存分配

度 串和对象实例 。 由大片的可利 用块或空闲块组成 , 中 堆 堆
的内存可 以按照 任意顺序 分配和释放 。而静态存 储分配要 求在 编译时 能知道 所有变量 的存 储要求 ,栈 式存储 分配要
求 在 过 程 的 入 口处 必 须 知道 所 有 的存 储 要 求 。
堆 内存用来 存放 由 nw 创建 的对象和 数组 , e 在堆 中分
是在函数的栈 内存 中分配 ,当在 一段代码 中定义 一个变 量
时 ,aa 就 在 栈 中 为 这 个 变 量 分 配 内存 空 间 , 当 超 过 变 量 Jv 的 作 用 域 后 ,aa 会 自 动 释 放 掉 为 该 变 量 分 配 的 内 存 空 Jv 间 , 内存 空 间 可 以 立 即 被 另 作 它 用 。 该
21 年 1 0 0 2月
电 脑 学 习
第6 期Jv aa中堆 与栈Fra bibliotek的 内存分 配
聂 芬‘ 王运生
摘 要 :堆与栈是Jv 用来在内存中存放数据的地方. aa 不能直接设置堆和栈。 aa自 由J v 动管理。 本文对堆内存与栈内存的分配
进 行 了阐 述 。
关键 词 :J a a ;堆内存 : v 栈内存 :分配 中 图分 类 号 :T 3 2 P 1 文献标识码 : A 文 章编 号 :0 2 2 2 2 1 0 — 13 0 10 — 4 2( 0 0) 6 0 2 - 2 He p a d S a k Al c to n M e o y o a a a n tc l a in i m r fJ v o
的一个名称 。引用变 量是普通 的变 量, 定义 时在栈 中分配 ,
引用 变 量 在 程 序 运 行 到 其 作 用 域 之 外 后 被 释 放 。而 数 组 和 对 象 本 身 在 堆 中 分 配 , 使 程 序 运 行 到 使 用 nw 产 生 数 组 即 e

操作系统实验-内存管理

操作系统实验-内存管理操作系统实验内存管理在计算机系统中,内存管理是操作系统的核心任务之一。

它负责有效地分配和管理计算机内存资源,以满足各种程序和进程的需求。

通过本次操作系统实验,我们对内存管理有了更深入的理解和认识。

内存是计算机用于存储正在运行的程序和数据的地方。

如果没有有效的内存管理机制,计算机系统将无法高效地运行多个程序,甚至可能会出现内存泄漏、内存不足等严重问题。

在实验中,我们首先接触到的是内存分配策略。

常见的内存分配策略包括连续分配和离散分配。

连续分配是将内存空间视为一个连续的地址空间,程序和数据被依次分配到连续的内存区域。

这种方式简单直观,但容易产生内存碎片,降低内存利用率。

离散分配则将内存分成大小相等或不等的块,根据需求进行分配。

其中分页存储管理和分段存储管理是两种常见的离散分配方式。

分页存储管理将内存空间划分为固定大小的页,程序也被分成相同大小的页,通过页表进行映射。

分段存储管理则根据程序的逻辑结构将其分成不同的段,如代码段、数据段等,每个段有不同的访问权限和长度。

接下来,我们研究了内存回收算法。

当程序不再使用分配的内存时,操作系统需要回收这些内存以便再次分配。

常见的内存回收算法有首次适应算法、最佳适应算法和最坏适应算法。

首次适应算法从内存的起始位置开始查找,找到第一个满足需求的空闲区域进行分配;最佳适应算法则选择大小最接近需求的空闲区域进行分配;最坏适应算法选择最大的空闲区域进行分配。

为了更直观地理解内存管理的过程,我们通过编程实现了一些简单的内存管理算法。

在编程过程中,我们深刻体会到了数据结构和算法的重要性。

例如,使用链表或二叉树等数据结构来表示空闲内存区域,可以提高内存分配和回收的效率。

在实验中,我们还遇到了一些实际的问题和挑战。

比如,如何处理内存碎片的问题。

内存碎片是指内存中存在一些无法被有效利用的小空闲区域。

为了解决这个问题,我们采用了内存紧缩技术,将分散的空闲区域合并成较大的连续区域。

jvm 对象分配过程

JVM对象分配过程1. 概述JVM(Java虚拟机)是Java程序运行的环境,它负责解释和执行Java字节码。

在Java程序中,对象是一种重要的数据结构,而JVM对于对象的分配和管理是非常关键的。

本文将深入探讨JVM对象分配的过程,包括对象的创建、内存分配、初始化和回收等。

2. 对象创建在Java程序中,通过new关键字来创建一个对象。

当执行new操作时,JVM会进行如下步骤: - 检查类是否已经加载到内存中,如果没有则进行类加载; - 在堆内存中为对象分配一块连续的内存空间; - 执行对象的构造方法进行初始化; - 返回对象引用。

3. 内存分配在JVM中,所有的对象都被分配在堆(Heap)上。

堆是一块动态分配的内存区域,用于存储所有的Java对象。

当执行new操作时,JVM会自动在堆上为对象分配内存空间。

3.1 对象头每个在堆上分配的对象都有一个与之对应的对象头(Object Header)。

对象头包含了一些必要信息,如: - 对象类型指针:用于确定该对象属于哪个类; - GC 标记位:用于标记对象是否可回收; - 锁标志位:用于实现同步机制。

3.2 内存分配方式JVM采用两种方式来进行内存分配:指针碰撞(Bump the Pointer)和空闲列表(Free List)。

3.2.1 指针碰撞指针碰撞是一种简单且高效的内存分配方式。

当堆中的空闲内存与已使用的内存之间有一块完全空闲的区域时,JVM可以通过移动一个指针来分配内存。

这个指针称为“指针碰撞指针”,它将堆分为两个部分:已使用的部分和未使用的部分。

3.2.2 空闲列表空闲列表是另一种常见的内存分配方式。

在这种方式下,JVM会维护一个链表,记录所有空闲的内存块。

当需要为对象分配内存时,JVM会遍历链表,找到合适大小的内存块,并将其从链表中移除。

4. 对象初始化在对象创建完成后,JVM会调用对象的构造方法进行初始化。

构造方法负责对对象进行初始化操作,如成员变量赋初值、执行其他初始化方法等。

JavaSE-4.1【IDEA;数组的定义、动态初始化、内存分配】

JavaSE-4.1【IDEA;数组的定义、动态初始化、内存分配】 1package day4;23/**4 * @author : haifei5 *6 * IDEA、数组定义、动态初始化、内存分配7 *8*/910public class Demo1_0520 {11public static void main(String[] args) {12/*13 IDEA快速⽣成语句:14 main函数=psvm+回车15输出语句=sout+回车16 IDEA快捷键:17 Ctrl+/ 单⾏注释18选中代码Ctrl+Shift+/ 多⾏注释19 Ctrl+Alt+L 格式化代码20 Ctrl+Y 删除光标所在⾏21 Ctrl+D 复制光标所在⾏的内容,插⼊光标位置下⾯22 Alt+Shift+上下箭头移动当前代码⾏23*/2425/*26数组通常存储同类型数据2728数组定义⽅式29 1、定义指定类型的数组:arr(推荐这种⽅式,可读性更好)30 int[] arr;31 double[] arr;32 char[] arr;3334 2、定义指定类型的变量:arr[]35 int arr[];36 double arr[];37 char arr[];38*/3940/*41数组初始化42 1、动态初始化43数组动态初始化就是只给定数组的长度,由系统给出默认初始化值44 2、静态初始化45*/4647//数组初始化之动态初始化48int[] arr = new int[5];49/*50等号左边:51 int:数组的数据类型52 []:代表这是⼀个数组53 arr:代表数组的名称54等号右边:55 new:为数组开辟内存空间56 int:数组的数据类型57 []:代表这是⼀个数组58 5:代表数组的长度59*/6061//数组变量访问⽅式:数组名,例如arr62//数组内部所保存数据的访问⽅式:数组名[索引],例如arr[0]63 System.out.println(arr); // [I@4554617c <--- 数组所在地址64 System.out.println(arr[0]); // 未赋值时,系统给出默认初始值065 System.out.println(arr[1]);66 System.out.println(arr[2]);6768/*69内存分配70 1、有关71堆内存:存储对象或者数组,new来创建的,都存储在堆内存。

String对象在java内存中的分配

String对象在java内存中的分配String对象在java内存中的分配之前⼀直存在⼀个疑问,在学习字符串常量池的时候查询资料,发现有的资料说new String("xxx")产⽣的字符串将分配在堆内存,⽽“xxx”形式的字符串赋值直接在常量池上进⾏查找和分配,但是也有资料说new String("xxx")在常量池和堆上都有分配,这段时间终于对其有所了解了。

String存储数据的细节在String中实际使⽤char数组进⾏存储(Java 9之后为byte数组,在这有其他考虑)也就是说数据最终存放在⼀个数组中,通过String这个数组最终指向的是⼀个typeArrayOopDesc的数组类型普通对象指针,⽽String类型的变量指向的是⼀个在堆内存上分配的instanceOopDesc,再由这个instanceOopDesc中的char[]指向前⾯所说的typeArrayOopDesc。

oop:普通对象指针,表⽰对象的实例信息,组成:HotSpot中的源码:class oopDesc {friend class VMStructs;private:javascript:void(0)volatile markOop _mark;union _metadata {Klass* _klass;narrowKlass _compressed_klass;} _metadata;...数据结构图:JVM对添加String的处理:在底层所说的常量池是⼀个类似HashMap的StirngTable,其中key使⽤stirng和其长度进⾏计算,value是⼀个Hashentry的结构,如果通过hash计算在StirngTable中对应索引的位置存在相同oop数据则直接返回此oop,否则创建⼀个HashEntry加⼊到StirngTable中。

在java 1.6时代,常量池存放在永久代,StirngTable的长度也默认固定在1009,会导致⼀个问题,也就是hash冲突,在之后移居到常量池移居到堆空间中,其长度也可以通过jvm参数进⾏调节,这也是取消永久代的⼀个原因。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

java内存分配研究基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。

引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量。

方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。

局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。

方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。

字符串常量在 DATA 区域分配,this 在堆空间分配。

数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小!哦对了,补充一下static在DATA区域分配。

其实是有规律的,只要你理解了这些个基本的原理:堆空间的话:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间。

另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。

但是速度快,也最灵活。

是向高地址扩展的数据结构,是不连续的内存区域。

这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。

堆的大小受限于计算机系统中有效的虚拟内存。

由此可见,堆获得的空间比较灵活,也比较大。

栈空间的话:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。

这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。

因此,能从栈获得的空间较小。

只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

由系统自动分配,速度较快。

但程序员是无法控制的。

ok,头会不会有点小晕,不会的话继续吧:JVM中的堆和栈JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。

堆栈以帧为单位保存线程的状态。

JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。

当线程激活一个Java方法,JVM就会在线程的Java 堆栈里新压入一个帧。

这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。

每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。

应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。

Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。

PS: 相关内容方法区在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。

类型信息是由类加载器在类加载时从类文件中提取出来的。

类(静态)变量也存储在方法区中。

jvm实现的设计者决定了类型信息的内部表现形式。

如,多字节变量在类文件是以big-endian存储的,但在加载到方法区后,其存放形式由jvm根据不同的平台来具体定义。

jvm在运行应用时要大量使用存储在方法区中的类型信息。

在类型信息的表示上,设计者除了要尽可能提高应用的运行效率外,还要考虑空间问题。

根据不同的需求,jvm的实现者可以在时间和空间上追求一种平衡。

因为方法区是被所有线程共享的,所以必须考虑数据的线程安全。

假如两个线程都在试图找lava的类,在lava类还没有被加载的情况下,只应该有一个线程去加载,而另一个线程等待。

方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。

同样方法区也不必是连续的。

方法区可以在堆(甚至是虚拟机自己的堆)中分配。

jvm可以允许用户和程序指定方法区的初始大小,最小和最大尺寸。

方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展java程序,一些类也会成为垃圾。

jvm可以回收一个未被引用类所占的空间,以使方法区的空间最小。

类型信息对每个加载的类型,jvm必须在方法区中存储以下类型信息:一这个类型的完整有效名二这个类型直接父类的完整有效名(除非这个类型是interface或是ng.Object,两种情况下都没有父类)三这个类型的修饰符(public,abstract, final的某个子集)四这个类型直接接口的一个有序列表类型名称在java类文件和jvm中都以完整有效名出现。

在java源代码中,完整有效名由类的所属包名称加一个".",再加上类名组成。

例如,类Object的所属包为ng,那它的完整名称为ng.Object,但在类文件里,所有的"."都被斜杠“/”代替,就成为java/lang/Object。

完整有效名在方法区中的表示根据不同的实现而不同。

除了以上的基本信息外,jvm还要为每个类型保存以下信息:类型的常量池( constant pool)域(Field)信息方法(Method)信息除了常量外的所有静态(static)变量常量池jvm为每个已加载的类型都维护一个常量池。

常量池就是这个类型用到的常量的一个有序集合,包括实际的常量(string,integer, 和floating point常量)和对类型,域和方法的符号引用。

池中的数据项象数组项一样,是通过索引访问的。

因为常量池存储了一个类型所使用到的所有类型,域和方法的符号引用,所以它在java程序的动态链接中起了核心的作用。

域信息jvm必须在方法区中保存类型的所有域的相关信息以及域的声明顺序,域的相关信息包括:域名域类型域修饰符(public, private, protected,static,final volatile, transient 的某个子集)方法信息jvm必须保存所有方法的以下信息,同样域信息一样包括声明顺序方法名方法的返回类型(或 void)方法参数的数量和类型(有序的)方法的修饰符(public, private, protected, static, final, synchronized, native, abstract的一个子集)除了abstract和native方法外,其他方法还有保存方法的字节码(bytecodes)操作数栈和方法栈帧的局部变量区的大小异常表类变量(Class Variables译者:就是类的静态变量,它只与类相关,所以称为类变量)类变量被类的所有实例共享,即使没有类实例时你也可以访问它。

这些变量只与类相关,所以在方法区中,它们成为类数据在逻辑上的一部分。

在jvm使用一个类之前,它必须在方法区中为每个non-final类变量分配空间。

常量(被声明为final的类变量)的处理方法则不同,每个常量都会在常量池中有一个拷贝。

non-final类变量被存储在声明它的类信息内,而final类被存储在所有使用它的类信息内。

对类加载器的引用jvm必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。

如果一个类型是由用户类加载器加载的,那么jvm会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。

jvm在动态链接的时候需要这个信息。

当解析一个类型到另一个类型的引用的时候,jvm需要保证这两个类型的类加载器是相同的。

这对jvm区分名字空间的方式是至关重要的。

对Class类的引用jvm为每个加载的类型(译者:包括类和接口)都创建一个ng.Class的实例。

而jvm必须以某种方式把Class的这个实例和存储在方法区中的类型数据联系起来。

你可以通过Class类的一个静态方法得到这个实例的引用// A method declared in class ng.Class:public static Class forName(String className);假如你调用forName("ng.Object"),你会得到与ng.Object对应的类对象。

你甚至可以通过这个函数得到任何包中的任何已加载的类引用,只要这个类能够被加载到当前的名字空间。

如果jvm不能把类加载到当前名字空间,forName就会抛出ClassNotFoundException。

(译者:熟悉COM的朋友一定会想到,在COM中也有一个称为类对象(Class Object)的东东,这个类对象主要是实现一种工厂模式,而java由于有了jvm这个中间层,类对象可以很方便的提供更多的信息。

这两种类对象都是Singleton的)也可以通过任一对象的getClass()函数得到类对象的引用,getClass被声明在Object类中:// A method declared in class ng.Object:public final Class getClass();例如,假如你有一个ng.Integer的对象引用,可以激活getClass()得到对应的类引用。

通过类对象的引用,你可以在运行中获得相应类存储在方法区中的类型信息,下面是一些Class类提供的方法:// Some of the methods declared in class ng.Class:public String getName();public Class getSuperClass();public boolean isInterface();public Class[] getInterfaces();public ClassLoader getClassLoader();这些方法仅能返回已加载类的信息。

相关文档
最新文档