JAVA内存了解一下

0x01 前言

要想java安全学的好,内存划分必不可少,对于分析java程序,了解内存的划分和运行机制对于安全研究是必不可少的一项技能,最近学习了一下,写了些笔记,分享一下。

0x02 Java内存划分

成员作用
方法区存放所有的class和static变量
每执行一个函数,就创建一个栈桢,并加入栈中(每个函数从调用到执行结束,其实是对应一个栈桢的入栈和出栈)
本地方法区为native方法服务
被所有线程共享的一块区域,在虚拟机启动时就被创建了,所有的对象实例和数组都是在堆上分配的
程序计数器当前线程所执行的字节码的行号指示器

0x03 栈

例如写一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {

static void say(String name) {

System.out.println("My name is " + name);

}

public static void main(String[] args) {

String name = "pino";
say(name);

}

}

以这段代码为例子,当程序运行的时候,首先会运行main函数,那么就会先生成一个main函数的栈桢,然后存放栈中,而栈桢中存放的是成员变量,也就是这里的name属性了,接着,调用了say函数,那么就会相应的生成一个say函数的栈桢。当say函数执行结束后,say的栈桢就会从栈中出栈,然后main函数运行结束,main的栈桢也出栈,最后栈中就空了,整个程序就结束了。

以上就是栈的运行机制了,总结一下就是成员变量放在自己的栈桢中,执行函数时把栈桢入栈,方法执行完毕后,该方法的栈桢出栈。

0x04 堆

堆的话,就像之前说的,所有的对象实例和数组都在堆上分配。
简单的理解就是所有new创建的对象实例都是在堆上进行分配的,因为数组也是new出来的。

例如如下代码

1
2
3
4
5
6
7
8
9
public class Person {

public static void main(String[] args) {

int ages[] = new int[] {1,2,3};

}

}

我这里创建了一个数组,那么具体的过程如下:

  1. 程序运行,调用main函数
  2. 生成main函数的栈桢,并存放到栈中
  3. main的栈桢有一个成员变量ages
  4. int[] ages 是声明一个int类型的数组
  5. new int[]{1,2,3} 是在堆中初始化数组(分配一块空间)
  6. 把初始化的地址赋值给ages
  7. 栈桢中的ages成员会指向堆中的那块空间

是的你没看错,赋值的是一个地址,不信你看

我把ages打印出来,结果确实是一个类似地址的字符串

那么具体点就是堆中存放这对象实例的地址,以及他的成员的值,比如这里它会存放:

位置0的值是1
位置1的值是2
位置2的值是3

当然,如果说有很多数组变量,那么内存空间可能会紧张,就会变成垃圾,等着垃圾回收器来回收。

0x05 垃圾回收机制

Java是自动垃圾回收机制,不像c语言,需要自己来回收。当JVM发现内存不够时,会自动清理无用对象

JVM的垃圾回收机制中,判断一个对象是否死亡,并不是根据是否还有对象对其引用,而是通过可达性分析。对象之间的引用可以抽象为树形结构,通过树根作为起点,从这些树根往下搜索,搜索走过的链称为引用链,当一个对象到GC ROOTs没有任何引用链相连,则证明这个对象是不可用的,该对象会被判定为可回收的对象。

0x06 总结

创建一个对象时,比如Person,方法区存放的是Person对象的字节码(Person.class)以及static等常量。
new的时候,堆中分配一块区域,有对象的地址和对象属性的值
栈中存放方法的栈桢,该栈桢存放成员变量,其值为实例化的对象的地址

本文标题:JAVA内存了解一下

文章作者:Pino-HD

发布时间:2018年05月30日 - 18:05

最后更新:2018年05月30日 - 18:05

原始链接:https://pino-hd.github.io/2018/05/30/JAVA内存了解一下/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!