您的位置:首页 > 编程语言 > Java开发

Java内存管理与反射机制

2017-03-26 21:51 253 查看

Java内存管理

理解Java程序运行时的内存管理,对很多相关技术的学习都有帮助,如Java的反射机制。

Java是纯面对对象语言,没有C/C++“全局变量”的概念,只有“成员变量”与“局部变量”的概念,所有的变量都存在于一个类中。

先看一段代码:

package com.kwws.demo.reflect;

public class A {
public static final String TAG = "A";
private int id;

public void setId(int newId) {
this.id = newId;
System.out.println(TAG + ".id=" + this.id);
}

public static void main(String[] args) {
String tag = A.TAG;//line 1
A a = new A();//line 2
a.setId(99);// line3
}
}


内存分配

Java程序运行时,涉及4块内存分配:栈内存(stack)、堆内存(heap)、代码段(code segment)、数据段(data segment),可以用下图表示:



栈内存(stack):存放局部变量的值:基础类型的值(
int a=1;
中a的值为1)与引用类型的地址(
A a = new A();
中a的值为内存地址0x0F2532,此时a称为A类对象的引用)

堆内存(heap):存放引用类型的对象的每个成员变量(如A类对象中的成员变量id),注意,不存放成员方法(如setId())。因此,创建一个引用类型的局部变量,在栈内存与堆内存各占用一块内存。

代码段(code segment):存放源程序代码,如类的成员方法(setId())和静态方法。注意,同一个类的不同对象共享该类的方法,并不是每创建一个对象就把成员方法复制一次。并且,对象使用方法时方法才被压入栈,不使用不占用内存。

数据段(data segment):存放静态变量和常量(和字符串常量)

main函数运行过程

1. line 1
String tag = A.TAG;
:数据段(data segment)一段内存保存常量
TAG="A"
;栈内存(stack)存放局部变量tag,内容为指向数据段中
TAG="A"
常量的存放地址(String为引用类型)

2. line 2
A a = new A();
:堆内存(heap)开辟了一块空间给A的一个对象,该对象中包含一段内存保存成员变量id(
new A()
)。栈内存(stack)中保存新局部变量a,内容为堆内存中A对象的地址(
A a = ...
),称“a是指向A的对象的引用”。

3. line 3
a.setId(99);
:栈内存保存基本类型int的值99;代码段中载入setId代码,并对a调用了该方法,方法中的
this
等于a,指向同一对象。

内存释放过程与垃圾回收

1. 修改a.id后,栈内存释放基本类型int 99的内存(立即出栈)

2. 栈内存中,局部变量a不被其他代码使用,a被释放(立即出栈)。此时,堆内存中A的对象不再被任何局部变量引用,GC随时可以回收该块堆内存(看GC心情)。

3. 局部变量tag不再使用,立即出栈。

4. 方法调用结束后,数据段与代码段内存中的内容被清空。

栈内存与堆内存在垃圾回收时的区别

可以看出,栈内存中的变量一旦不使用则立即释放占用的内存;而堆内存中的变量只有在不被任何变量引用时,垃圾收集器才会“看心情”回收这块内存。

Java反射机制

概念:

Java的反射机制是在编译时并不确定哪个类被加载,而是在程序运行的时候才确定。Java反射允许在程序运行过程中通过API来取得已知class类的相关信息,可以动态地生成此类,并调用其方法或修改其成员变量(甚至是声明为private的方法或变量)。

作用:

将要使用的类A.class未编写完时,或者说开发初期不知道某个功能的具体实现时,可以使用反射,到程序运行期间(开发完成后)动态加载A.class。如支持扩展插件的程序。

在Android开发中,可以使用Java反射机制使用被@hide标记隐藏起来的API。

使用:

上文A.class中的main方法,可以等价替换为如下代码:

import java.lang.reflect.Method;//引入反射包

public static void main(String[] args) {
try {
Class A = Class.forName("com.kwws.demo.reflect.A");//根据类名找到指定类A
Object a = A.newInstance();//实例化A类的一个对象a

Method setId = A.getMethod("setId", int.class);//找到A类的setId方法,参数为int类
setId.setAccessible(true);//令该方法可访问

setId.invoke(a, 99);//对a调用setId方法,参数为99
} catch (Exception e) {
e.printStackTrace();
}
}


上述反射机制的使用,对应了Java内存管理的两点:

方法查找
Method setId = A.getMethod("setId", int.class);
说明了:Java的成员方法是针对类级别A而言,而非对象a;

setId.invoke(a, 99);
将A的对象a作为参数传入该方法,说明类的方法在内存中只拷贝一份,而不是每个对象一份。

通过Class类对象的getMethod/getField方法可以获得类中包含private修饰在内的所有成员,由此可实现对private成员、@hide隐藏成员的访问。

参考

Java内存分配全面浅析

Java反射机制的学习(概念、相关API)

Java(Android)反射机制实例(实践代码)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: