一.内存结构
1. jvm 的内存结构
a.栈:线程私有的(每个线程都有自己的栈内存,生命周期与线程相同)
程序计数器(Program Counter Register)
:记录当前线程所执行的字节码的信号指示器本地方法栈(Native Method Stacks)
:非 Java 语言编写的方法(Native 方法)执行时需要的栈内存栈帧(Stack Frame)
:有多个,每个栈帧对应一次方法的调用,用来存储局部变量表、方法参数、操作栈、动态链接、方法出口等信息。- 每一个方法被调用到执行完成的过程,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
b.堆:线程共享的(在虚拟机启动时就创建,需要注意线程安全问题)
-
堆是 Java 虚拟机所管理的内存中最大的一块;
-
新创建的对象实例、数组都存放在堆内存中;
-
Java 7开始,串池也会使用堆内存(之前串池用的是方法区内存),为什么会发生这样的变化呢?
因为系统内字符串占用很大的空间,而方法区几乎不进行垃圾回收,方法区内存就会很容易出现内存溢出问题 -
堆内存有垃圾回收处理机制,可以将堆内存分为新生代(伊甸园、2个幸存区)和老年代。
可以自己设置初始堆内存大小,最大堆内存,新生代堆内存,幸存区比例,TLAB内存 。
TLAB
:新生代的一块特殊的部分,大小约为整个新生代的1%,每个线程可以在新生代中申请一块内存(tlab),使得此线程变为私有的,其他线程不能访问。在tlab中创建新的对象时,不需要加锁,这样就可以提升对象创建的效率。
c.方法区:线程共享的(Java 7实现叫做永久代)
- 类版本、字段定义、方法定义;
- 静态变量:Java 7 开始,使用了堆内存;
- 常量池:串池从Java 7 开始,使用了堆内存。
- 即时编译器(JIT)生成的代码:
即时编译器
是一个把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器(processor)的指令的程序。
Java优点之一就是你只需要写和编译一次程序。在任何平台上,Java都会将编译好的字节码解释成能被特定的处理器所理解的指令(即一次编译,到处运行)。但尽管如此,Java虚拟机一次只能处理一条字节码指令。在特定的系统平台上使用Java即时编译器,能把字节码编译成特定系统的代码(虽然这个程序最初已经在这个平台上被编译过)。
一旦代码被JIT编译器(重)编译后,它在电脑上通常就会运行地更快。
2.非虚拟机内存(非堆内存)
Java 8 以后,移除了永久代,换为了元空间(meta space)
例如整个机器有8g的内存,jvm占用了500m,其他程序占用了 500m,呢么剩下的7g的内存都可以被元空间使用。