Java内存管理与反射机制
Posted wkw1125
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java内存管理与反射机制相关的知识,希望对你有一定的参考价值。
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)反射机制实例(实践代码)
以上是关于Java内存管理与反射机制的主要内容,如果未能解决你的问题,请参考以下文章