0x01 前言
要想java安全学的好,内存划分必不可少,对于分析java程序,了解内存的划分和运行机制对于安全研究是必不可少的一项技能,最近学习了一下,写了些笔记,分享一下。
0x02 Java内存划分
成员 | 作用 |
---|---|
方法区 | 存放所有的class和static变量 |
栈 | 每执行一个函数,就创建一个栈桢,并加入栈中(每个函数从调用到执行结束,其实是对应一个栈桢的入栈和出栈) |
本地方法区 | 为native方法服务 |
堆 | 被所有线程共享的一块区域,在虚拟机启动时就被创建了,所有的对象实例和数组都是在堆上分配的 |
程序计数器 | 当前线程所执行的字节码的行号指示器 |
0x03 栈
例如写一段代码
1 | public class Person { |
以这段代码为例子,当程序运行的时候,首先会运行main函数,那么就会先生成一个main函数的栈桢,然后存放栈中,而栈桢中存放的是成员变量,也就是这里的name属性了,接着,调用了say函数,那么就会相应的生成一个say函数的栈桢。当say函数执行结束后,say的栈桢就会从栈中出栈,然后main函数运行结束,main的栈桢也出栈,最后栈中就空了,整个程序就结束了。
以上就是栈的运行机制了,总结一下就是成员变量放在自己的栈桢中,执行函数时把栈桢入栈,方法执行完毕后,该方法的栈桢出栈。
0x04 堆
堆的话,就像之前说的,所有的对象实例和数组都在堆上分配。
简单的理解就是所有new创建的对象实例都是在堆上进行分配的,因为数组也是new出来的。
例如如下代码
1 | public class Person { |
我这里创建了一个数组,那么具体的过程如下:
- 程序运行,调用main函数
- 生成main函数的栈桢,并存放到栈中
- main的栈桢有一个成员变量ages
- int[] ages 是声明一个int类型的数组
- new int[]{1,2,3} 是在堆中初始化数组(分配一块空间)
- 把初始化的地址赋值给ages
- 栈桢中的ages成员会指向堆中的那块空间
是的你没看错,赋值的是一个地址,不信你看
我把ages打印出来,结果确实是一个类似地址的字符串
那么具体点就是堆中存放这对象实例的地址,以及他的成员的值,比如这里它会存放:
位置0的值是1
位置1的值是2
位置2的值是3
当然,如果说有很多数组变量,那么内存空间可能会紧张,就会变成垃圾,等着垃圾回收器来回收。
0x05 垃圾回收机制
Java是自动垃圾回收机制,不像c语言,需要自己来回收。当JVM发现内存不够时,会自动清理无用对象
JVM的垃圾回收机制中,判断一个对象是否死亡,并不是根据是否还有对象对其引用,而是通过可达性分析。对象之间的引用可以抽象为树形结构,通过树根作为起点,从这些树根往下搜索,搜索走过的链称为引用链,当一个对象到GC ROOTs没有任何引用链相连,则证明这个对象是不可用的,该对象会被判定为可回收的对象。
0x06 总结
创建一个对象时,比如Person,方法区存放的是Person对象的字节码(Person.class)以及static等常量。
new的时候,堆中分配一块区域,有对象的地址和对象属性的值
栈中存放方法的栈桢,该栈桢存放成员变量,其值为实例化的对象的地址