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隐藏成员的访问。

参考

  1. Java内存分配全面浅析
  2. Java反射机制的学习(概念、相关API)
  3. Java(Android)反射机制实例(实践代码)

以上是关于Java内存管理与反射机制的主要内容,如果未能解决你的问题,请参考以下文章

Java反射机制

类加载机制与反射

Java基础java类加载过程与反射机制

java8--类加载机制与反射(java疯狂讲义3复习笔记)

类加载机制与反射

Java学习注解和反射超详细笔记