Java 的实例对象都分配在堆里面heap,实例的引用都在stack里面
new() 一个java对象肯定会向heap申请该对象的存储空间,当heap没有空间分配给这个对象的时候就报OutMemoryErroy ,内存溢出异常,对象里面有方法局部变量则会会为其创建临时的堆栈信息,运行时动态的分配地址内存,存取效率低
一个对象在内存中,其实就是一个堆栈地址所指向的一个堆内存,对象中的方法就是java语言编译后的机器指令操作内存中数据 堆内存可以分为 新生代(Young Generation)和老年代(Old Generation),java的gc回收主要就是回收堆上面的内存,采用分代垃圾回收,当堆内存不够分配,gc回收堆新生代垃圾,如果回收新生代后,内存还是不够则回收老年代垃圾(永久代PermGen不是在堆里面是在方法区,jdk1.8之前是要配置该区域大小,jdk1.8后不需要设置该区域大小,默认最大值没有限制)
stack 栈,栈中数据所指向的堆地址可能是相同的,存取效率高,栈中数据的生命周期是固定的方法执行完,栈中的数据就会被清理,但是栈中数据对应的堆内存里面的数据没有被清理(等待java的垃圾回收进程,回收没有任何引用指向该堆内存地址),如果长时间没有回收则,会引起内存泄露(内存被应该回收的对象占有,所以内存溢出和内存泄露都只会出现在heap堆上面的)
java中的数据类型分为两种
- java的8个基本类型**byte char boolean short int long float doubble ** ,这些比如int a=3,long b=5L,这些都是存的字面值,及3和5不是存的引用,变量的大小和生命周期是可知的(这些值都是固定在程序的代码块中,退出代码块后,变量的生命周期就结束了)因此为了提高读取效率和内存的维护,java把基本类型的变量及自动变量的字面值,放在stack中,放在stack中还有一个好处,就是stack里面的数据是可以共享的,比如 int a=3;int b=3; 在创建int a = 3的时候,会到stack里面扫描看看是否存在stack里面是否存在字面值为3的地址,有直接返回内存地址,没有则重新开辟一个新地址 所以在创建int b=3的时候,会直接扫描到字面值为3的内存地址,然后把地址返回给b 基本类型的地址引用与非基本类型的引用是不相同的
jvm的内存模型
线程共享的分为,heap堆和methodArea方法区, 堆上面做过介绍了,下面介绍一下methodArea方法区 方法区里面保存类的变量信息(静态信息),类的访问修饰符,类的类型信息(接口还是类),类的路径信息,类的直接父类信息,类的字段信息,类的方法信息还有常量池 常量池 主要用于存储两大类数据,字面值常量和符号引用量 字面值长量:字符串文本,final修饰变量 常量池中有一张CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值 符号引用量则属于编译原理方面的概念,包括了如下三种类型的常量: 类和接口的权限定名 字段名称和描述符 方法名称和描述符
jdk1.8将字符串长量由方法区迁移到了堆中, 提高java堆内存的监控和垃圾回收 字符串长量在方法区的永久代,容易内存泄漏,永久代的数据,javagc不会回收,java gc只会回收heap上面的垃圾