内存管理 & 值传递和引用传递 & GC
内存管理
-
Java程序所使用的内存是由Java虚拟机进行管理、分配的。
-
Java虚拟机对内存的管理:
1、Java虚拟机在内存中划分的区域:
- 方法区(默认最大容量是64M)又划分出静态区、常量池
- 静态区:存放静态变量、程序运行期间,类名首次出现的时候,静态变量就被分配内存了。
- 常量池:存放字面量对象。字面量会增加不会改变。
- 堆区(默认最大容量是256M):存放实例对象的属性、数组值;每new一次就在堆中分配一次内存。
- 栈区(虚拟机栈 默认最大容量是1M):先进后出;程序调用方法时会在栈中划分一块栈帧,栈帧中的内存供局部变量( 包括基本类型和局部类型)使用,方法调用结束后会收回此栈帧占用的内存。
2、Java内存区域图:
- 加载顺序:
- 当一个类的类名首次出现时,JVM将类的字节码(.class)加载到方法区中
- 为类的静态属性分配(静态区中的)内存
- 调用类的静态块,为静态属性初始化;静态块只在加载类后调用一次。
- 实例化对象时(在堆中)为对象的属性分配内存(如果没有实例化就不分配)
- 调用构造方法,为属性初始化
- 运行顺序
- 程序从main()方法开始运行,JVM在栈中为方法开辟内存空间
- 方法名入栈后,栈帧指向该方法
- 为方法中的局部变量在栈中分配内存
- 调用下一个方法时,重复执行1~3步
- JVM释放某方法的内存空间时,该方法中的局部变量内存也被回收
- main()最先执行,一般都在栈底,调用其他方法时,其他方法再往上压,那么,什么时候main()被回收?
5、变量的内存分配
- 静态变量:静态区,与类同生死,类加载后就有初始值了;
- 实例变量:堆中,与对象同生死,对象new出来后才有初始值;
- 局部变量:栈中,使用前要先初始化,系统不管的。
值传递和引用传递
-
基本数据类型采用值传递;如果方法的参数是基本数据类型,Java虚拟机会拷贝参数的值,访如新的栈帧
-
引用数据类型采用引用传递;如果方法的参数是引用数据类型,Java虚拟机会拷贝引用对象在堆中的地址,放入新的栈帧中。
-
引用传递是为了提高运行效率,减少内存浪费
-
例子:
class Student{ String name; int age; } public class ValueTransform { public static void main(String[] args) { Student stu = new Student(); stu.name = "思恩"; stu.age = 18; changeInt(stu.age); System.out.println("调用changeInt(int)的结果是:" + stu.age); changeStudent(stu); System.out.println("调用changeStudent(Student)的结果是:" + stu.age); } public static void changeInt(int i) { i ++; } public static void changeStudent(Student stu1) { stu1.age ++; } }输出结果:
调用changeInt(int)的结果是:18调用changeStudent(Student)的结果是:19
垃圾回收机制(gc)
- 由Java虚拟机内部的垃圾回收线程自动对不再使用的内存进行回收,这种机制称为垃圾回收机制。
- 回收顺序:
- 虚引用 幽灵引用,存在感很弱很弱
- 弱引用 回收机制来查两次还是弱就回收,内存近满也把它回收了
- 软引用 还存在耦合
- 强引用 基本不回收,比如new出来的东西
1、在堆、栈、方法区中的垃圾回收
- 堆:主要回收不再被引用的对象数据所占用的内存
- 栈:栈会在方法调用时划分栈帧,方法调用结束时回收栈帧,不需要垃圾回收
- 方法区:回收不再被对象引用的结构所占用的内存
2、Runtime类的使用
| 返回类型 | 方法名称 | 作用 |
|---|---|---|
| Runtime | getRuntime | 获取Runtime对象 |
| long | totalMemory() | 获取JVM分配给程序的内存数量 |
| Long | freeMemory() | 获取当前可用的内存数量 |
| Long | maxMemory() | 获取Java虚拟机可以申请到的最大内存数量 |
| void | gc() | 建议Java虚拟机进行垃圾回收 |
- gc()方法只会建议Java虚拟机进行垃圾回收,但是不保证一定会进行