问:你了解java的内存模型嘛?
内存简介:
地址空间划分
- 内核空间(主要是操作系统程序和C运行时的空间,包含链接硬件、调度程序、提供联网、虚拟内存等逻辑和基于C的进程)
- 用户空间(java实际运行时使用的空间,32位系统最多访问3G,内核代码可以访问所有物理内存。64位系统可以访问超过512G,内核代码同样可以访问所有物理内存)
jvm内存模型--jdk8及以后
- 线程私有:程序计数器、虚拟机栈、本地方法栈
- 线程共享:MetaSpace(元空间)、java堆
程序计数器(是一块较小的内存空间,线程私有)
- 当前线程所执行的字节码行号指示器(逻辑)
- 改变计数器的值来选取下一条需要执行的字节码指令(分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器)
- 和线程是一对一的关系即“线程私有内存”(jvm是多线程运行的,为了确保切换线程后能恢复到每个线程原来运行的位置就需要私有程序计数器)
- 对java方法技术,如果是Native方法则计数器位Undefined
- 不会发生内存泄漏(因为只是记录了行号)
java虚拟机栈(stack,线程私有)
- java方法执行的内存模型
- 包含多个栈帧(方法运行的基础数据结构,包含局部变量表、操作栈、动态链接、返回地址等入栈到出栈的过程。栈帧持有局部变量、部分结果、参与方法的调用与返回,方法调用结束时帧才会被销毁。)
局部变量表和操作数栈
- 局部变量表:包含方法执行过程中的所有变量(包括this引用、所有方法参数、其他局部变量)
- 操作数栈:入栈、出栈、复制、交换、产生消费变量(在执行字节码指令过程中被用到,这种方式类似于原生cpu寄存器,大部分jvm字节码把时间花费在操作数栈的操作上,因此局部变量的数组和操作数栈的操作指令通过字节码频繁执行)
实例:
编译二进制,再反编译字节码 (这里javac的时候遇到了个小问题,因为中文注释的原因,GBK不可映射字符 所以加上 -endcoding UTF-8 解决问题)
descript:(II)I 表示描述方法:有2个int参数 返回int, flags 标记public的 static的
stack=2 操作数栈 深度2 ,locals 本地变量是3 ,args_size参数大小是2个
然后就是一堆进栈出栈的指令
LineNumberTable是代码的行号对应字节码行号
执行add(1,2)过程:
问:递归为什么会引发java.lang.StackOverflowError异常?
递归过深,栈帧数超出虚拟栈深度 解决方法就是减少递归次数或者循环替换递归
虚拟机栈过多会引发java.lang.OutOfMemoryError异常
虚拟机栈不需要GC回收,用完就会释放掉
本地方法栈:
- 与虚拟机栈相似,主要作用于标注了native的方法