`
luzl
  • 浏览: 572227 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java变量作用域内存

    博客分类:
  • Java
阅读更多
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

结果两个打印的结果是一样的,当然我是分两次运行的,不是在一一起运行的。所以感觉没有区别,可能从作用域来说作用域越小,越容易被回收吧,所以并不是变量定义在外面科学,感觉应该是定义在里面更好一些,因为生命周期短,容易被回收。
但是从CPU角度讲,可能写在外面的话,创建新变量的过程省了,是不是更加节省CPU执行单元?

另外有以下代码可以跑着看看内存什么时候被回收,你可以试试写在外面好还是写在里面好,我跑着试了试,感觉都一样:
package com.ibm.partnerworld.vic.testcase;

import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str02="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str03="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str04="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str05="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str06="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str07="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str08="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str09="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str10="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str11="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str12="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str13="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str14="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str15="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
		try {
			System.out.println("Created");
			Thread.currentThread().sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("I was reused!");
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
			holdMemory=new HoldMemory();
			System.out.println("total=>"+Runtime.getRuntime().totalMemory());
			System.out.println("free=>"+Runtime.getRuntime().freeMemory());
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

分享到:
评论
22 楼 sswh 2010-05-23  
这儿再做一个简单的试验,看一下:

public class Hello {
	void testMemory() throws Exception {
		logMemoryInfo();
		for (int i = 0; i < 1; i++) {
			Object ref = new int[500000];
		}
		logMemoryInfo();
	}

	void logMemoryInfo() throws Exception {
		System.out.print("before gc: usedmem: ");
		System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
		System.gc();
		Thread.sleep(3000);
		System.out.print("after gc: usedmem: ");
		System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
	}

	public static void main(String[] args) throws Exception {
		new Hello().testMemory();
	}
}


在我的机子上,运行结果如下:
before gc: usedmem: 215616
after gc: usedmem: 155336
before gc: usedmem: 2155352
after gc: usedmem: 137008

把上面的代码改成:
void testMemory() throws Exception {
	logMemoryInfo();
	Object ref;
	for (int i = 0; i < 1; i++) {
		ref = new int[500000];
	}
	logMemoryInfo();
}


运行结果不变。
21 楼 sswh 2010-05-23  
zl198751 写道


所谓字节码完全相同而位置不一定,就说程序是一致的,我看很大程度上是不对的。
就是因为变量i和holdMemory在局部变量表的位置不同,所以程序在运行时的内存状态是不一致的。

当holdMemory定义在外面的话,循环结束后内存中依然保留为holdMemory开辟的内存。但是定义在里面的话内存在循环结束后就被回收了。


对于JVM而言,没有块级别的作用域,在进入方法体时,局部变量所占用的内存就已经在Java栈上分配好了,
直到方法结束,Java栈上的局部变量内存才被回收,不存在方法执行途中回收的问题。

不过要注意,这儿的内存仅仅是指Java栈上的内存(对于对象而言,也就是非primitive的变量,仅仅是对象的引用)
而Java对象所分配的内存,是位于Java堆中。

就楼主的例子,不论holdMemory定义在循环外、或者循环内,都不影响每一次循环中创建的new HoldMemory()的回收,因为,每一次循环的执行,都重写了同一个变量引用,从而导致上一次循环holdMemory对象引用的丢失,即原则上,每开始一个新的循环,上一个循环创建的holdMemory对象都是可回收的,这和holdMemory变量定义在循环体内外无关。



~~~~~~~~~~~~~~~~~~~~~~~
小结一下:
1、Java栈上的局部变量(primitive值、或者对象引用)所占用的内存,在方法结束后,随Java栈的回收而回收,与变量定义在循环体内外无关;
2、Java对象(使用堆内存)在每次循环后,自动丢弃上一次循环所保留的对象引用,从而该对象处于可回收状态。这也和变量定义在循环体内外无关;
3、局部变量定义在循环体内外,仅仅对代码的可读性有影响。对代码执行没有区别。
20 楼 zl198751 2010-05-13  
sswh 写道
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。



所谓字节码完全相同而位置不一定,就说程序是一致的,我看很大程度上是不对的。
就是因为变量i和holdMemory在局部变量表的位置不同,所以程序在运行时的内存状态是不一致的。

当holdMemory定义在外面的话,循环结束后内存中依然保留为holdMemory开辟的内存。但是定义在里面的话内存在循环结束后就被回收了。
19 楼 gml520 2010-05-13  
sswh 写道
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊


是jclasslib


我用jclasslib 写了一个 NetBeans 插件。http://www.iteye.com/topic/665511
18 楼 feigme 2010-05-13  
sswh 写道
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。


同意sswh
在运行时,声明根本不会执行
所以定义在循环外或内没有什么区别
不过,还是遵守最小作用域原则
17 楼 opmic 2010-05-12  
flyfan 写道
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊

强烈要求公布一下

16 楼 icyiwh 2010-05-12  
  在外面不要初始化(哪怕是null)
15 楼 szmq2 2010-05-11  
我认为编程工作和做事的方法是一样的,想了一个不算恰当的比方:
    会议室需要招开10次不同的会议,每次会议完后你需要清理会场
    以下设备其实是不用重复清理的:座子、椅子(没必要每次开完会后把座子椅子拿回去,然后又重新摆好)
   以下设备其实是需要每次清理的:一次性水杯(这些不能给其它到会人员重复使用吧)
14 楼 my13901 2010-05-11  
引:Java是编译型语言不是解释型语言。

我晕,Java不是编译型语言是解释型语言。
13 楼 zxsy007x 2010-05-11  
谢谢 学习了
12 楼 prowl 2010-05-11  
字节码工具很nice  多谢sswh了
11 楼 sswh 2010-05-11  
另外,你的第二段代码如果去掉holdMemory初始化为null操作,

也就是下面两种情况:
	public void testname() throws Exception {
		HoldMemory holdMemory;//去掉=null操作
		for (int i = 0; i < 50000; i++) {
			holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}



	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory = new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
	}


所生成的字节码是完全相同的。(只是变量i和holdMemory在局部变量表的位置不同,对程序运行没有任何影响)

可见:对Java而言,局部变量声明在循环体内、或者循环体外没有任何区别。
10 楼 sswh 2010-05-11  
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊


是jclasslib
9 楼 J-catTeam 2010-05-11  
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}

结果两个打印的结果是一样的,当然我是分两次运行的,不是在一一起运行的。所以感觉没有区别,可能从作用域来说作用域越小,越容易被回收吧,所以并不是变量定义在外面科学,感觉应该是定义在里面更好一些,因为生命周期短,容易被回收。
但是从CPU角度讲,可能写在外面的话,创建新变量的过程省了,是不是更加节省CPU执行单元?

另外有以下代码可以跑着看看内存什么时候被回收,你可以试试写在外面好还是写在里面好,我跑着试了试,感觉都一样:
package com.ibm.partnerworld.vic.testcase;

import junit.framework.TestCase;

class HoldMemory{
	String str01="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str02="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str03="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str04="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str05="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str06="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str07="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str08="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str09="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str10="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str11="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str12="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str13="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str14="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	String str15="12111111111111111111111111111111111111111111111111111111111111111111111111111111111";
	
	public HoldMemory(){
		try {
			System.out.println("Created");
			Thread.currentThread().sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
		System.out.println("I was reused!");
	}
}

public class MemoryCase extends TestCase{

	public void testname() throws Exception {
		HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
			holdMemory=new HoldMemory();
			System.out.println("total=>"+Runtime.getRuntime().totalMemory());
			System.out.println("free=>"+Runtime.getRuntime().freeMemory());
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


在外面·那么只有一个引用
里面·引用的个数就是循环的次数
8 楼 flyfan 2010-05-11  
lbfhappy 写道
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊

强烈要求公布一下
7 楼 luzl 2010-05-10  
sswh 写道
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


Java是编译型语言不是解释型语言。
上面两段代码生成的字节码,对于方法testname() 而言,其局部变量都是3个:this、i、holdMemory,
即使看上去“HoldMemory holdMemory”是在循环体内声明的,holdMemory变量也仍然只有一个。

这两段代码生成的字节码的唯一区别是 第一段代码前面多了局部变量的初始化操作。

第一段代码对应的字节码:


第二段代码对应的字节码:



用你这个就很好理解为什么两个在内存的占用上是一样的了,请教一下你是怎么看字节码的?
6 楼 lbfhappy 2010-05-10  
楼上查看字节码的工具是什么啊.能介绍一下不?
看上去好像是netbeans的感觉啊
5 楼 sswh 2010-05-10  
luzl 写道
我一直在想一个问题,做循环的时候变量在循环体内定义省内存还是变量在循环体外定义省内存,于是做了下实验:
	public void testname() throws Exception {
		for (int i = 0; i < 50000; i++) {
			HoldMemory holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


和下面的code进行比较:
	public void testname() throws Exception {
                HoldMemory holdMemory=null;
		for (int i = 0; i < 50000; i++) {
		     holdMemory=new HoldMemory();
		}
		System.out.println(Runtime.getRuntime().freeMemory());
		
	}
}


Java是编译型语言不是解释型语言。
上面两段代码生成的字节码,对于方法testname() 而言,其局部变量都是3个:this、i、holdMemory,
即使看上去“HoldMemory holdMemory”是在循环体内声明的,holdMemory变量也仍然只有一个。

这两段代码生成的字节码的唯一区别是 第一段代码前面多了局部变量的初始化操作。

第一段代码对应的字节码:


第二段代码对应的字节码:


4 楼 chcc1023 2010-05-10  
这种小测试感觉是没什么区别,但是拿到项目中应用呢?
3 楼 liwenjie 2010-05-10  
首先需要清楚内存分配有堆栈和堆,就好比String s=new String("2")有几个对象的问题,s放在堆栈上,new的对象放在堆上,在循环内定义变量,变量在堆栈上出了作用域就被销毁了,而定义在外,变量出了循环没有被销毁

相关推荐

    黑马程序员 - Java基础教学 - 03 - 变量的作用域、for循环、break、continue、内存结构

    ### 黑马程序员Java基础教学知识点详解 #### 一、变量的作用域与生命...以上内容涵盖了Java基础教学中的关键概念,包括变量作用域、循环结构、流程控制以及内存管理的基本原理,是学习Java编程不可或缺的基础知识。

    Java变量和对象的作用域

    Java变量和对象的作用域是编程时非常重要的概念,它们决定了变量和对象的可见性和生命周期。在Java中,作用域由代码中的大括号 `{}` 定义,这被称为语句块。一旦变量或对象在某个作用域内声明,它们就只能在该作用域...

    JAVA程序设计课件-变量的作用域.pptx

    \n\n总之,理解并熟练掌握JAVA中的变量作用域是成为一名合格的JAVA程序员的基础,也是OCJA认证考试的关键内容之一。通过深入学习和实践,可以有效地提升编程技能,为未来的JAVA开发工作打下坚实的基础。

    基于java变量和作用域以及成员变量的默认初始化(详解)

    基于java变量和作用域以及成员变量的默认初始化 Java 变量和作用域是 Java 编程语言的基础概念,了解变量和作用域的概念对编写高效、可靠的 Java 程序非常重要。本文将详细介绍 Java 变量和作用域,以及成员变量的...

    关于Java变量的可见性问题Java开发Java经验技巧共

    1. **作用域(Scope)**:变量的作用域是变量可见的区域。一旦离开变量的作用域,变量就无法访问。例如,局部变量只在其定义的代码块内有效。 2. **封装(Encapsulation)**:Java鼓励通过设置访问修饰符来控制类的...

    Java 成员变量和局部变量

    1. **生命周期与作用域**: 局部变量在其声明的代码块内部有效,一旦离开该块,变量就不再存在。它们不会在方法调用之间保持值。 2. **必须初始化**: 与成员变量不同,局部变量在声明时必须立即初始化,否则编译器将...

    java成员变量覆盖问题

    如果两个变量之间存在重写关系,那么在同一作用域内定义相同的`final`变量将会导致编译错误。下面是修改后的代码: ```java public class Test1 { final String color = "黄色"; public void getMes() { System....

    accp5.0java第二章变量(二)

    在Java中,有四种主要的作用域:方法作用域、块作用域、类作用域和实例(或对象)作用域。局部变量(如方法内的变量)只在其声明的代码块内有效,而类变量(静态变量)在整个类中都可访问。实例变量(非静态变量)...

    8.java变量.zip

    本资料包"8.java变量.zip"聚焦于Java中的变量,特别是通过8.java变量.docx文档详细阐述了这一主题。以下是关于Java变量的详尽解析。 1. **变量类型** - **基本类型**:Java有八种基本数据类型,包括整型(byte, ...

    Java中堆内存与栈内存分配浅析

    当函数执行完毕或者变量的作用域结束,Java虚拟机(JVM)会自动释放栈内存中分配的空间,使得这部分内存可以被其他变量重新使用。栈内存的管理高效,因为它采用先进后出(LIFO)的数据结构,分配和回收都非常快速。 ...

    TENSORFLOW变量作用域(VARIABLE SCOPE)

    变量作用域允许开发者定义变量的作用域名称,这样在同一个作用域内创建变量时,该变量会自动获得相应的作用域前缀,从而实现变量重用,而无需创建全局变量。这样不仅提高了代码的可维护性,也使得模型的结构更加清晰...

    java内存原理.doc

    这些变量在函数的栈内存中分配,当超过变量的作用域后,Java 会自动释放掉为变量分配的内存空间,该内存空间可以立刻被另作他用。栈内存的特点是:自动释放、快速分配、空间小。 堆内存用于存放由 new 创建的对象...

    Java中堆内存和栈内存详解

    堆内存中的对象生命周期不受作用域限制,仅在不再被任何引用变量指向时才会被垃圾回收机制回收。 #### 六、JVM中的堆和栈 Java虚拟机(JVM)是基于堆栈架构的。对于每一个新创建的线程,JVM都会为其分配一个独立的...

    Java栈内存与堆内存

    Java把内存划分成两种:一种是栈内存,一种...当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

    java变量的五种方式t共2页.pdf.zip

    在这个文档“java变量的五种方式t共2页.pdf.zip”中,我们预计会涵盖Java中的五种主要的变量类型及其用法。尽管实际内容无法在当前环境中直接查看,但根据常规的Java教程,我可以提供一个详细的概述。 1. **基本...

    Java 变量类型的知识分享

    以下是对Java变量类型的详细讲解: 1. 基本数据类型(Primitive Data Types) Java中的基本数据类型分为两大类:数值类型和非数值类型。 - 数值类型: - 整型:byte(1字节,-128到127)、short(2字节,-32,...

    JavaScript变量类型以及变量作用域详解

    在深入学习和应用JavaScript编程时,理解变量类型以及变量作用域是至关重要的。JavaScript是一种动态类型的语言,意味着变量可以存储不同类型的值,并且不需要在声明时指定类型。在JavaScript中,所有的变量都存储为...

    Java成员变量类变量局部变量的区别共5页.pdf.zip

    - 分配内存:成员变量和类变量在对象创建或类加载时分配内存,局部变量在进入其作用域时分配内存。 理解这三种变量类型有助于编写清晰、高效的Java代码。正确地使用它们可以帮助优化内存使用,提高程序性能,并...

Global site tag (gtag.js) - Google Analytics