JVM内存分配理解,求指导

飞火流云 2015-08-29 07:33:22
加精

public class MemoryTest {

int[] a = new int[0] ;

public void set(){
a = new int[10] ;
for(int i =0;i<10;i++)
a[i] = i ;
}

/**
* @param args
*/
public static void main(String[] args) {
MemoryTest mt = new MemoryTest() ;
mt.set();

for(int v:mt.a)
System.out.println(v); //这里a数组的对象应该是分配在堆上还是栈上? 如果是在堆上,会否因为set函数执行完被回收么? 还是说只要mt对象存在,该数组里面的对象就存在?
System.gc(); //这里垃圾回收似乎没有起到作用
System.out.println("-------------");

for(int v:mt.a)
System.out.println(v);
}

}
...全文
1558 33 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
dengvdeng 2015-09-14
  • 打赏
  • 举报
回复
围观
wjlsmail 2015-09-14
  • 打赏
  • 举报
回复
a在堆上被分配
shouxuan32 2015-09-13
  • 打赏
  • 举报
回复
谢谢楼主分享。来支持你了
  • 打赏
  • 举报
回复
恩,说的挺有道理的
raodi03 2015-09-10
  • 打赏
  • 举报
回复
解开者 2015-08-31
  • 打赏
  • 举报
回复
基本类型属于常量,存在方法区里,在装箱的时候会发生常量复用。因为某种原因,能复用的只有-128~127也就是一个字节之内的整数。所以两个值为100的Integer比较引用是相等的,改成200就不行。 根据是否被持有引用决定对象是否被gc是不可靠的,因为可能存在两个对象互相保存对方的一个引用,但它们没有外部引用的情况。这时候实际上它们已经是无用的了,但因为被持有引用所以无法gc。目前的多数实现都是由jvm持有一个根引用,用类似遍历一个引用树的方法,然后gc掉所有没被遍历到的对象。 new int[0]作为一个对象和其他对象一样也有对象头,所以还是要占地方的。
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
引用 3 楼 lonrence 的回复:
set方法执行时,为a分配了10个对象 这个不对分配的不是对象,是基本数据类型int,是字面值,字面值定义在某个程序块里面,程序块退出后,字段值就消失了
不好意思这里我不明白,为什么这里会是基本类型? int的装箱操作这里不会被执行么? 如果是基本类型,那是否应该分配在栈上,那函数执行完后就应该消失了啊? 还是说数组在新分配对象时,也是在堆上进行分配的?
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
另外,如果有人能给出C++下的内存分配机制,即使用和这块类似的代码,就更好了。。。。可以做个对比。
0萌萌哒0 2015-08-30
  • 打赏
  • 举报
回复
引用 2 楼 cloudeagle_bupt 的回复:
[quote=引用 1 楼 lonrence 的回复:] 内存可以简单划分为: 1、栈:局部变量、对象的引用名、数组的引用名 2、堆:new出来的东西(如:对象的实体,数组的实体),因此含成员变量,什么是new出来的东西?就是引用数据类型 3、方法区:包含常量池 4、静态域:声明为static的变量 详细的参见下图 通过给出的类 a是MemoryTest类的成员变量 main方法是程序的入口开始 new MemoryTest(),内存将在堆中开辟空间,这个空间包含了成员变量a,a指向了在堆中的一个空间,存放new出来的数组 mt是对象的引用名,存储在栈; mt.set(),调用方法set(),在堆空间存放new int[10],给数组成员赋值 垃圾回收先可以理解为 栈空间的用完就会释放,就是程序运行到变量的作用范围之外就会自动释放 堆空间由JVM虚拟机回收,实例对象没有任何引用指向它,垃圾回收就会回收它 因此,a数组的对象应该是分配在堆上,其回收根据MemoryTest对象的回收进行回收 共同学习,不对指正
嗯,我理解也是如此,这里关键是set方法的执行过程, 首先, a作为数组引用,是mt的成员变量,被分配在堆上,set方法执行时,为a分配了10个对象,都在堆上,当然set方法是在栈上执行的,执行完后set部分被回收,但是a作为引用仍然存在,并指向刚刚分配的10个对象,在mt引用未被释放前,a一直存在,因此a指向的对象也存在,不会被占用或者回收。 这是我的理解。[/quote] 相差不多,只要你把“分配10个对象”的说法改为”分配了长度为10个int的数组“就更对了。 gc的实现现在基本使用的是gc root判定方法,即从几个根引用区域(调用栈,常量区)出发,所能引用到的对象都不会被回收。 另,引用计数方法存在一个弱点,不能回收循环引用(即仅有对象a, b之间相互保留引用)的对象,故现在gc不再单独使用这种方法。
Ant_Shen 2015-08-30
  • 打赏
  • 举报
回复
当对象调用set方法时,对象初始化时a所引用的空间(new int[0])失去引用,而堆内存中(new int[10])空间会被a引用,gc处理的是初始化时a引用的空间,因为对象没有引用,所以对象还存在,所以对象的成员引用还存在,所以堆中(new int[10])的空间还存在。楼主的理解是对的
scmod 2015-08-30
  • 打赏
  • 举报
回复
没看懂... a这个不是全局变量么...为啥要给回收...
forDream_ 2015-08-30
  • 打赏
  • 举报
回复
a分配在堆内存中,而mt中有a的引用,因此,a不会被回收。 我理解JVM的垃圾回收机制是,只会回收孤立的对象,所谓孤立就是没有任何地方引用的对象,就认为是不会被再次使用(其实没无法再次使用),因此被释放掉。所以,只要一个对象还有某个地方对其存在引用就不会被释放(我感觉有点类似于Windows 32 API中的LoadLibrary,只有当引用计数变为0的时候,Windows才会真正将文件从内存中释放)
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
引用 25 楼 lonrence 的回复:
== 操作符 比较的是值 基本数据类型的值是其字面量值 引用数据类型的值是其引用的对象实例的首地址值,既然引用同一个对象,自然用==比较就是true了 int a = 100 ; Integer b = 100 ; System.out.println(a==b?1:0); System.out.println(b==a?1:0); 结果都是1,至于编译器会怎么编译,我也不知道,我觉得应该是转成基本数据类型比较,毕竟操作符主要操作基本类型

		Integer a = 100 ;
		Integer b = 100 ;
		System.out.println(a.intValue()==b.intValue()?1:0);
		System.out.println(a==b?1:0);

		Double d1 = 1.2366666666666666  ;
		Double d2 = 1.2366666666666666  ;
		System.out.println(d1.doubleValue()==d2.doubleValue()?1:0);
		System.out.println(d1==d2?1:0);
试了下,还真是,你说的是对的!
  • 打赏
  • 举报
回复
引用 24 楼 cloudeagle_bupt 的回复:
[quote=引用 21 楼 lonrence 的回复:] [quote=引用 18 楼 cloudeagle_bupt 的回复:] [quote=引用 17 楼 lonrence 的回复:] [quote=引用 13 楼 cloudeagle_bupt 的回复:] 另外,我在测试中还发现一个有意思的问题:

		int[] a =new int[0];
		System.out.println(a==null?1:0);
 
		int[] b = null;
		System.out.println(b==null?1:0);
输出结果: 0 1 我不明白这里Java数组是如何设置的,但是int[0] 这样的写法确实避免了空指针,但是内存确实进行了分配么?
int[] a =new int[0];是在堆空间开辟了一个空间,存放数组数据,这个数组的长度为0;在栈空间开辟空间存储一个int[]类型的变量a,a的值是new出来的堆空间的首地址值(a指向了一个对象实例)。 int[] b = null;在栈空间开辟空间存储一个int[]类型的变量b,b没有值(b没有指向了一个对象实例)。[/quote] 你的意思是,作为一个数组对象,即使其长度为0,其指向的堆中仍然占据一定大小,作为这个数组对象的元数据之类的。[/quote] 我认为是这样的,不过这个new int[0]的大小是多少我同样有疑问,new int[2] 分配的空间是2个int类型8个字节,new int[0]是多少,同求高人指点[/quote] 17楼说的是对的,看下这里: http://www.ibm.com/developerworks/cn/java/j-codetoheap/ Java 数组对象详解 数组对象(例如一个 int 值数组)的形状和结构与标准 Java 对象相似。主要差别在于数组对象包含说明数组大小的额外元数据。因此,数据对象的元数据包括: 类:一个指向类信息的指针,描述了对象类型。举例来说,对于 int 字段数组,这是 int[] 类的一个指针。 标记:一组标记,描述了对象的状态,包括对象的散列码(如果有),以及对象的形状(也就是说,对象是否是数组)。 锁:对象的同步信息,也就是说,对象目前是否正在同步。 大小:数组的大小。 。。。 按照里面的描述,图3的int数组应该有5个元素,共占用320bit内存, 160位用于保存元信息,剩下160位存储5个int元素,[/quote] 嗯,new int[0] 没有给元素分配空间,因为长度为0,但是有开销,谢谢,解决了我的一个疑问
  • 打赏
  • 举报
回复
== 操作符 比较的是值 基本数据类型的值是其字面量值 引用数据类型的值是其引用的对象实例的首地址值,既然引用同一个对象,自然用==比较就是true了 int a = 100 ; Integer b = 100 ; System.out.println(a==b?1:0); System.out.println(b==a?1:0); 结果都是1,至于编译器会怎么编译,我也不知道,我觉得应该是转成基本数据类型比较,毕竟操作符主要操作基本类型
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
引用 21 楼 lonrence 的回复:
[quote=引用 18 楼 cloudeagle_bupt 的回复:] [quote=引用 17 楼 lonrence 的回复:] [quote=引用 13 楼 cloudeagle_bupt 的回复:] 另外,我在测试中还发现一个有意思的问题:

		int[] a =new int[0];
		System.out.println(a==null?1:0);
 
		int[] b = null;
		System.out.println(b==null?1:0);
输出结果: 0 1 我不明白这里Java数组是如何设置的,但是int[0] 这样的写法确实避免了空指针,但是内存确实进行了分配么?
int[] a =new int[0];是在堆空间开辟了一个空间,存放数组数据,这个数组的长度为0;在栈空间开辟空间存储一个int[]类型的变量a,a的值是new出来的堆空间的首地址值(a指向了一个对象实例)。 int[] b = null;在栈空间开辟空间存储一个int[]类型的变量b,b没有值(b没有指向了一个对象实例)。[/quote] 你的意思是,作为一个数组对象,即使其长度为0,其指向的堆中仍然占据一定大小,作为这个数组对象的元数据之类的。[/quote] 我认为是这样的,不过这个new int[0]的大小是多少我同样有疑问,new int[2] 分配的空间是2个int类型8个字节,new int[0]是多少,同求高人指点[/quote] 17楼说的是对的,看下这里: http://www.ibm.com/developerworks/cn/java/j-codetoheap/ Java 数组对象详解 数组对象(例如一个 int 值数组)的形状和结构与标准 Java 对象相似。主要差别在于数组对象包含说明数组大小的额外元数据。因此,数据对象的元数据包括: 类:一个指向类信息的指针,描述了对象类型。举例来说,对于 int 字段数组,这是 int[] 类的一个指针。 标记:一组标记,描述了对象的状态,包括对象的散列码(如果有),以及对象的形状(也就是说,对象是否是数组)。 锁:对象的同步信息,也就是说,对象目前是否正在同步。 大小:数组的大小。 。。。 按照里面的描述,图3的int数组应该有5个元素,共占用320bit内存, 160位用于保存元信息,剩下160位存储5个int元素,
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
引用 22 楼 lonrence 的回复:
[quote=引用 20 楼 cloudeagle_bupt 的回复:] [quote=引用 19 楼 lonrence 的回复:] [quote=引用 16 楼 cloudeagle_bupt 的回复:] [quote=引用 15 楼 lonrence 的回复:] int的装箱操作这里不会被执行么? 自动装箱自动拆箱是Java新特性,主要是减少了编译时基本类型与包装器类型之间的转换操作,是个偷懒行为,其实编译器还是要做转换操作。 比如 向一个容器list中添加元素,必须要添加引用数据类型 如果调用方法list.add(3);,编译器不会报错,会将3自动装箱成Integer对象;如果你从容器中取出这个元素,进行算术运算的话 int j = list.get(0) + 1; 由于运算只能在基本类型之间进行(个别除外),这个时候编译器会自动拆箱为基本类型进行运算。 所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱,在这个例子中你声明的是int[],自然这个数组的元素都是int类型了。 说到底,Java是个强类型语言,声明的类型必须和操作所需的类型一致,否则要么编译器替你做了,要么编译的时候就会报错。
"所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱" 这句赞下,我明白了,前几天遇到个问题,关于同步加锁的问题,当时使用int i 作为被锁的对象,同步条件是i增加或者减少,当时很奇怪为何不起作用,后来发现是因为i加减过程中必须被当成对象来处理,因此被初始化成了Integer对象,自然加锁的目标就不是同一个对象了,因而起不到作用,这里你说的应该是对的,即数组作为一个对象,分配时为基本类型,不需要用到对象操作,因而没有装箱拆箱! [/quote] 这里还要注意一个问题,包装规范,介于-128~127的short和int类型,包装时会包装到一个固定对象中,char<=127,boolean和byte类型也是[/quote] 你这里的"固定对象"是什么意思? 难道double和float不是么?[/quote] 固定对象意思是同一个对象实例 比如 Integer a = 100; 包装一个Integer对象 Integer b = 100; 包装到同一个Integer对象 a == b 为true[/quote] 你的意思是说: 对于较长的基本类型,封装的对象可能不是同一块内存? 这里怎么判断 a == b 执行的是地址比较还是值比较? 我看了下Integer的源码,没有类似重写==号的代码, 如果使用: int a = 100 ; Integer b = 100 ; System.out.println(a==b?1:0); System.out.println(b==a?1:0); 这个好像无法区分比较的是100这个对象还是地址。
  • 打赏
  • 举报
回复
引用 20 楼 cloudeagle_bupt 的回复:
[quote=引用 19 楼 lonrence 的回复:] [quote=引用 16 楼 cloudeagle_bupt 的回复:] [quote=引用 15 楼 lonrence 的回复:] int的装箱操作这里不会被执行么? 自动装箱自动拆箱是Java新特性,主要是减少了编译时基本类型与包装器类型之间的转换操作,是个偷懒行为,其实编译器还是要做转换操作。 比如 向一个容器list中添加元素,必须要添加引用数据类型 如果调用方法list.add(3);,编译器不会报错,会将3自动装箱成Integer对象;如果你从容器中取出这个元素,进行算术运算的话 int j = list.get(0) + 1; 由于运算只能在基本类型之间进行(个别除外),这个时候编译器会自动拆箱为基本类型进行运算。 所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱,在这个例子中你声明的是int[],自然这个数组的元素都是int类型了。 说到底,Java是个强类型语言,声明的类型必须和操作所需的类型一致,否则要么编译器替你做了,要么编译的时候就会报错。
"所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱" 这句赞下,我明白了,前几天遇到个问题,关于同步加锁的问题,当时使用int i 作为被锁的对象,同步条件是i增加或者减少,当时很奇怪为何不起作用,后来发现是因为i加减过程中必须被当成对象来处理,因此被初始化成了Integer对象,自然加锁的目标就不是同一个对象了,因而起不到作用,这里你说的应该是对的,即数组作为一个对象,分配时为基本类型,不需要用到对象操作,因而没有装箱拆箱! [/quote] 这里还要注意一个问题,包装规范,介于-128~127的short和int类型,包装时会包装到一个固定对象中,char<=127,boolean和byte类型也是[/quote] 你这里的"固定对象"是什么意思? 难道double和float不是么?[/quote] 固定对象意思是同一个对象实例 比如 Integer a = 100; 包装一个Integer对象 Integer b = 100; 包装到同一个Integer对象 a == b 为true
飞火流云 2015-08-30
  • 打赏
  • 举报
回复
引用 19 楼 lonrence 的回复:
[quote=引用 16 楼 cloudeagle_bupt 的回复:] [quote=引用 15 楼 lonrence 的回复:] int的装箱操作这里不会被执行么? 自动装箱自动拆箱是Java新特性,主要是减少了编译时基本类型与包装器类型之间的转换操作,是个偷懒行为,其实编译器还是要做转换操作。 比如 向一个容器list中添加元素,必须要添加引用数据类型 如果调用方法list.add(3);,编译器不会报错,会将3自动装箱成Integer对象;如果你从容器中取出这个元素,进行算术运算的话 int j = list.get(0) + 1; 由于运算只能在基本类型之间进行(个别除外),这个时候编译器会自动拆箱为基本类型进行运算。 所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱,在这个例子中你声明的是int[],自然这个数组的元素都是int类型了。 说到底,Java是个强类型语言,声明的类型必须和操作所需的类型一致,否则要么编译器替你做了,要么编译的时候就会报错。
"所以说,在你声明的类型与代码要操作的所需要的类型有冲突的时候,才发生自动装箱拆箱" 这句赞下,我明白了,前几天遇到个问题,关于同步加锁的问题,当时使用int i 作为被锁的对象,同步条件是i增加或者减少,当时很奇怪为何不起作用,后来发现是因为i加减过程中必须被当成对象来处理,因此被初始化成了Integer对象,自然加锁的目标就不是同一个对象了,因而起不到作用,这里你说的应该是对的,即数组作为一个对象,分配时为基本类型,不需要用到对象操作,因而没有装箱拆箱! [/quote] 这里还要注意一个问题,包装规范,介于-128~127的short和int类型,包装时会包装到一个固定对象中,char<=127,boolean和byte类型也是[/quote] 你这里的"固定对象"是什么意思? 难道double和float不是么?
  • 打赏
  • 举报
回复
引用 18 楼 cloudeagle_bupt 的回复:
[quote=引用 17 楼 lonrence 的回复:] [quote=引用 13 楼 cloudeagle_bupt 的回复:] 另外,我在测试中还发现一个有意思的问题:

		int[] a =new int[0];
		System.out.println(a==null?1:0);
 
		int[] b = null;
		System.out.println(b==null?1:0);
输出结果: 0 1 我不明白这里Java数组是如何设置的,但是int[0] 这样的写法确实避免了空指针,但是内存确实进行了分配么?
int[] a =new int[0];是在堆空间开辟了一个空间,存放数组数据,这个数组的长度为0;在栈空间开辟空间存储一个int[]类型的变量a,a的值是new出来的堆空间的首地址值(a指向了一个对象实例)。 int[] b = null;在栈空间开辟空间存储一个int[]类型的变量b,b没有值(b没有指向了一个对象实例)。[/quote] 你的意思是,作为一个数组对象,即使其长度为0,其指向的堆中仍然占据一定大小,作为这个数组对象的元数据之类的。[/quote] 我认为是这样的,不过这个new int[0]的大小是多少我同样有疑问,new int[2] 分配的空间是2个int类型8个字节,new int[0]是多少,同求高人指点
加载更多回复(13)

62,635

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧