在C++领域,关于C++的内存存储,结构等等,有一本书:深度探索C++对象模型,讲解的非常透彻。
而Java确把这一工作交给了虚拟机来处理。
我们首先来看看关于内存的问题。
1.问题:
1)java 的局部变量存在那里,类型怎么存放的?
2)static 变量
3)final 变量
4)method是存放的那里的
5)class类 & 内部类 是如何存放
2.java虚拟机运行是数据区
2.1程序计数器
线程隔离数据,记录当前线程执行的位置。
PC计数器
由于Java虚拟机的多线程,是由线程轮换的方式分配CPU的。所以线程切换后,需要恢复到原来执行的地方
没有线程都需要一块内存来记录这个执行位置,此内存是线程私有的。
2.2Java虚拟机栈
这个栈就是我们常见的java 面试题中一般会问 heap & stack 的区别。 这里的stack就是我们说的虚拟机栈。
这个栈是存放每个method执行的时候的局部变量表,动态连接,方法出口等信息。
局部变量表存放:局部变量类型,已经基本类型的变量。
栈中可以理解为存放对象的引用,为对象本身存放在堆。
如果申请的内存不够,就会OOM。
2.3本地方法栈
这个东西同Java虚拟机栈非常类似,这里存放这native方法的一些信息。
2.4Java堆
千呼万唤始出来,对象实例就是存放在这个地方。也是java内存最大的地方。
GC的主要工作就是释放这里不用的内存。
这块区域为所有线程共享。所以android就会有activity或者service在非前台状态下,是否低优先级的情况。
2.5方法区
这块地方存放的就是已经加载的类信息,静态变量,常量等。
String abc = “abc”; 这后面的“abc”就存放在这里。
2.5.2 运行时常量区
java在非编译阶段,也可以是一种常量的形式,比如:
String str = "abc"+"d";
它会生成“abcd”常量在运行时候。
3.HotStot虚拟机对象探秘
java对象的创建过程分成2步:
1)加载类信息,存放在方法区
2)根据类信息,分配内存存放对象,内存在堆区。
分配内存的常见方式:
1)假设内存在堆区分为2块,一块是已经在使用的,一块是还没有分配的,这样我只要设置一个指针,来区分这两块区域。
当分配新的内存时,移动指针即可。
2)内存区域是不连续的,这样我只能在空闲的内存中,找到一块足够大的内存来分配,这种为空闲列表的方式。
但是,当分配内存时,会有同步的问题。
要么保证分配操作是原子操作,或者就是给每个线程优先分配一小块内存(Thread Local Allocation Buffer)
这个东西在android looper机制的时候,有所体现。
接下来就是对对象进行设置:
对象是那个类的实例,如何才找到对象的元数据信息,对象的hash表,对象的GC分布年龄等。
从程序员的角度看,一个对象在内存中已经创建,但是init的方法还没有执行。对象还没有按照程序员的意愿进行初始化。
3.1 对象的内存布局
在HotStot中,对象的布局可以分为3块:对象头,实例数据,和对其填充。
对象头根据虚拟机的位数,32bit或者64bit。
这部分被以极限的算法,来存储不少的信息。
实例数据部分,存的是真正有效的信息,这部分会存父类的信息和子类的信息。
3.2对象的访问方式
reference 指向的是另一个类的对象,所以它指向的实际是堆中的另一块内存而已。
HotSopt使用的是第二种方式。