参考视频
JVM架构总览图
程序计数器
程序计数器,物理上用寄存器实现。
作用: 记住下一条JVM指令的执行地址
特点:
1 是线程私有的,随着线程的创建而创建,随着线程的消息而消息
2 是一小块内存
3 唯一不会内存溢出的地方
栈
介绍
栈:程序运行需要的内存空间
虚拟机栈: 每个线程运行时所需要的内存
数据结构:先进(压栈)后出(出栈)
一个栈可以看成多个栈帧组成,每个栈帧可以看成每个方法的运行时需要的内存(参数,局部变量,返回地址等)
定义:
1 每个线程运行时所需要的内存,成为虚拟机栈
2 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
3 每个线程只能有一个活动栈帧,活动栈帧即当前正在执行的那个方法
问题辨析:
1 垃圾回收是否涉及栈内存?
答:不需要。 每次方法结束后都会出栈,自动被回收,所以不需要垃圾回收。
2 栈内存分配越大越好吗?
答:不是。内存是有限的,栈内存越大,线程越少。
Linux/MacOs/Oracle Solaris : 栈内存大小默认1024k
-Xss1024k
3 方法内的局部变量是否线程安全?
答:如果方法内 局部变量没有逃离方法的作用范围,它是线程安全的。如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
栈内存溢出
- 栈帧过多,即调用的方法过多,最容易产生的:递归调用(测试2w多次递归会报错)
- 栈帧过大,不太容易出现
本地方法栈(native method stacks)
不是由java代码编写的方法,java用本地方法调用底层的c或c++使用的方法
给本地的方法的运行提供内存空间
线程私有
堆(Heap)
介绍
通过new关键字,创建对象都会使用堆内存
特点:
- 它是线程共享的,堆中对象都需要考虑线程安全问题
- 有垃圾回收机制
堆内存溢出
配置堆内存大小:-Xmx4G
方法区
定义
方法区是一个逻辑上的概念,也被称为非堆(Non-Heap),一般用来存储类加载信息、static变量、JIT实时编译缓存的代码、常量池(Constants Pool)等。不同版本的Java其方法区的实现方式不同,在JDK 8之前,采用的是“永久代”来实现方法区,而在JDK 8之后则是采用MetaSpace(元空间)的方式来实现
- 共享性: 方法区与Java堆一样,是各个线程共享的内存区域。
- 创建和内存空间: 方法区在JVM启动时被创建,实际的物理内存空间和Java堆一样,可以是不连续的。
- 大小和可扩展性: 方法区的大小,就像堆空间一样,可以选择固定大小或可扩展。
- 溢出问题: 方法区的大小决定了系统能够保存多少个类。如果系统定义了太多的类,导致方法区溢出,虚拟机将抛出内存溢出错误,例如
java.lang.OutOfMemoryError: PermGen space
或java.lang.OutOfMemoryError: Metaspace
。 - 释放: 关闭JVM将释放方法区的内存空间。
方法区是用于存储类信息、常量池、静态变量等数据的区域,对于Java虚拟机的正常运行和类加载等都具有重要作用。
jdk8之后 字符串常量和静态变量移到堆中