网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
如果一个线程执行一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法(本地方法),这个计数器的值则为空,此内存区域是唯一一个在Java的虚拟机规范中没有规定任何OutOfMemoryError异常情况的区域。
2.虚拟机栈(Java Virtual Machine Stacks)
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧用来存储局部变量表,操作数栈,动态链接,方法出口等信息,参与方法的调用和返回,每一个方法从调用到执行完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型即所有引用类型的父类,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
异常状况:我们知道,一个线程拥有一个自己的栈,这个栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss 参数可以设置虚拟机栈大小),当栈帧一直在执行入栈操作时,线程请求的栈的深度大于虚拟机栈所允许的深度时,就会发生栈溢出(StackOverflowError),一般造成这个问题的原因是程序中方法被循环调用没有退出。虚拟机栈也可以动态扩展,但当超过Java虚拟机规范中所规定的长度时,就会报OutOfMemoryError(内存溢出)异常。
- StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度,将会抛出此异常。
- OutOfMemoryError:当可动态扩展的虚拟机栈在扩展时无法申请到足够的内存,就会抛出该异常。
3.本地方法栈(Native Method Stack)
本地方法栈和java虚拟机栈的功能相似,java虚拟机栈用于管理Java函数(字节码)的调用,而本地方法栈用于管理本地Native方法的调用,但不是由Java实现的,而是由C实现的。本地方法栈中方法使用的语言,使用方式,数据结构没有强制要求。有的虚拟机比如(HotSpot)直接就将虚拟机栈和本地方法栈合二为一。
与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
参数:使用-Xss参数减少栈内存容量。
4.Java堆(Heap)
堆是JVM里最大的一块内存区域,被所有线程共享,在虚拟机启动时创建,此区域的目的就是存放对象实例和数组,为所有创建的对象和数据分配内存空间,且每次分配的空间是不定长的。在Heap 中分配一定的内存来保存对象实例,实际上只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在Stack中)。
Java堆是垃圾收集管理的主要区域,也被称为GC堆,由于现在收集器基本都采用分代收集算法,所以Java的堆中还可以分为新生代,老年代,永久代,JDK1.8之后取消了永久代;其中新生代又划分为Eden空间,From Survivor空间,To Survivor空间。无论怎么划分都是为了更好的回收,分配,利用内存。
Java堆可以处于物理上不连续,逻辑上连续的存空间,Java堆在实现时,既可以是固定大小的,也可以是可拓展的,并且主流虚拟机都是按可扩展来实现的(通过-Xmx(最大堆容量) 和 -Xms(最小堆容量)控制)。如果在堆中没有内存完成实例分配,并且堆也无法再拓展时,将会抛出 OutOfMemoryError 异常。
5.方法区(Method Area)
也被称为永久区,与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,堆的一个逻辑部分,Non-Heap(非堆),其中运行时常量池就在方法区,对永久区的GC回收,主要是针对常量池的回收和对类型的卸载。
Java堆是 Java代码可及的内存,是留给开发人员使用的;而非堆(Non-Heap)是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存 (如JIT编译后的代码缓存)、每个类结构 (如运行时常量池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
根据Java虚拟机规范的规定,当方法区无法满足内存分配的需求时,将抛出 OutOfMemoryError 异常。
5.1运行时常量池(Runtime Constant Pool)
运行时常量池时方法区的一部分,用来存放编译器生成的各种字面量(常量和字符串)和符号引用(类和接口的符号引用、字段名称和描述的符号引用、方法名称和描述的符号引用),当常量池无法再申请到内存时也会抛出 OutOfMemoryError 异常。运行时常量池相对于Class文件常量池的一个重要特征是具备动态性。
Java语言并不要求常量一定只有编译期才能产生,运行期间也可能将新的常量放入池中,比如字符串的手动入池方法intern()。
6、直接内存(Direct Memory)
直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但这部分内存也被频繁运用,而却可能导致OutOfMemoryError异常出现。
这个我们实际中主要接触到的就是NIO,在NIO引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能避免在Java堆和Native堆中来回复制数据,在一些场景里显著提高性能。这块内存不受Java堆大小限制,但受本机总内存限制,可以通过MaxdirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;
二、栈和栈帧
虚拟机栈是线程私有的,每个方法在执行时都会创建一个栈帧,加入虚拟机栈,方法执行结束后,所对应的栈帧出栈。栈帧用来存储局部变量表,操作数栈,动态链接,返回地址,附加信息等,参与方法的调用和返回,每一个方法从调用到执行完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
1、栈:
又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。其特性是先进后出。栈是线程私有的,生命周期跟线程相同,当创建一个线程时,同时会创建一个栈,栈的大小和深度都是固定的。方法参数列表中的变量,方法体中的基本数据类型的变量和引用数据类型的引用都存放在栈中,成员变量和对象本身不存放在栈中。运行时,成员函数的局部变量引用也存放在栈中。栈的变量随着变量作用域的结束而释放,栈中的变量随着方法的调用而创建,当方法执行结束后,jvm会自动释放内存,不需要GC回收。栈不是全局共享的,每个线程创建一个栈,该线程只能访问其对应的栈数据。栈内存的大小是在编译期就确定了的。
2、栈帧:
一个栈中可以有多个栈帧,栈帧随着方法的调用而创建,随着方法的结束而消亡。该栈帧中存储该方法中的变量,原则上各个栈帧之间的数据是不能共享的,但是在方法间调用时,jvm会将一方法的返回值赋值给调用它的栈帧中。每一个方法调用,就是一个压栈的过程,每个方法的结束就是一个出栈的过程。压栈都会将该栈帧置于栈顶,每个栈不会同时操作多个栈帧,只会操作栈顶,当栈顶操作结束时,会将该栈帧弹出,同时会释放该栈帧内存,其下一个栈帧将变为栈顶。栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
在一个线程执行的任何时刻,都只会有一个帧是处于激活的。这个帧被称为当前帧,与之对应的方法被称为当前方法,方法所在的类被称为当前类,此时用到的本地变量数组和操作数栈也都是当前帧的。
1、局部变量表:是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量,所需内存空间(最大容量)在编译期间就已经确定,方法运行时不会改变,局部变量表的容量以**变量槽(Slot)**为最小单位,虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0到局部变量表最大的Slot数量。在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程的。
2、操作数栈:也叫操作栈,后入先出栈,线程执行时使用到的数据的存储空间,操作数栈的最大深度在编译时就已经确定。方法刚开始执行时,操作数栈是空的,在方法执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,出栈/入栈操作。方法执行中进行算术运算或者调用其他的方法进行参数传递的时候是通过操作数栈进行的。
在概念模型中,两个栈帧是相互独立的。但是大多数虚拟机的实现都会进行优化,令两个栈帧出现一部分重叠。令下面栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一块,这样在方法调用的时候可以共用一部分数据,无需进行额外的参数复制传递。
3、动态链接:一些方法区的引用,Class文件的运行时常量池会保存大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,如果符号引用一部分是在类加载阶段或者第一次使用的时候转化为直接引用,那么这种转换称为静态解析,如果是在运行期间转换为直接引用,那么这种转换就称为动态连接。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
涵盖了95%以上Go语言开发知识点,真正体系化!**
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新