Java Virtual Machine
java虚拟机,主要分为:
1)类加载子系统
2)jvm运行时数据区(内存)
方法区、堆、栈、程序计数器、本地方法栈
3)执行引擎
通过new创建的对象一定是在“堆内存”,
反之,通过“字符串直接量“方式创建的对象,在方法区中
String类的intern()方法,会把“字符串”加入到“常量池”中
通过new String(),创建的“字符串对象”是在堆内存中的,所以可以用intern()方法,会把“字符串”加入到“常量池”中
1.运行时数据区,
java虚拟机在执行java程序的时候,把内存分配了若干个数据区域。
2.jvm运行时结构图
3.程序计数器(Program Counter Register)
它是程序 “指令”的集合,(包括,分支,跳转,循环,异常处理,线程恢复)
它是一块较小的内存空间,
他的运行速度最快。
线程私有。
4.java虚拟机栈
线程私有。
虚拟机栈,描述的是java方法执行的内存模型,每个方法在执行时,都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等。
栈中的每一栈帧,都对应一个java方法,
可以说,栈帧 “进栈” 和 “出栈”的过程,就是一个java方法的生命周期
每个线程中,都对应一个jvm栈
栈中的每一个栈帧,就是一个java方法
人们经常说的“栈”,就是指的,java"虚拟机栈",或者是 java虚拟机栈中的“局部变量表”。
局部变量表,中,存放了 8种基本类型 和 对象的“引用地址”,
局部变量表的,内存空间,在编译期间,已经可以完全确认,运行期间是不会改变内存空间的大小的。
操作数栈:
相当于,数学考试中的“草稿纸”一样,用于记录局部变量表中数据的运算过程。
java虚拟机栈会抛出:
StackOverflowError
OutOfMemoryError
5.本地方法栈。
线程私有。
本地方法栈,与,虚拟机栈,类似。
虚拟机栈,为执行java方法,而服务。
本地方法栈,为使用Native方法服务。
本地方法栈中的对象,就是不通过java编写的,可能是C++,或其他语言编写的。
6.java堆。
线程共享。
存放对象实例。
java堆是“垃圾收集器”负责的主要区域,因此也叫“GC堆”,(Garbage Collected Heap)
java堆内存:可以分为,“新生代”和“老年代”
年轻代、老年代
年轻代:垃圾回收频率多
老年代:垃圾回收频率少
年轻代,分为:Eden区(占80%的空间),survivor区(占20%的空间)
survivor区又分为:from区、to区,各占10%。
一般情况下,新生的“对象”分配在“Eden区”,当Eden区没有空间分配时,jvm将发起一次Minor
GC,如果对在“第一次”Minor GC后任然存活,并且能被Survivor区容纳(就是Survivor能不
能装的下),对象就会被移动到Survivor区中,并且对象年龄设为1岁(jvm给每一个对象义
了“对象年龄计数器”),对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当对
象到达一定年龄(默认15岁),就会被移动到“老年代”。
对象晋升“老年代”的值可以设置:-XX:MaxTenuringThreshold=15
小结:长期存活的对象将进入老年代
让大对象直接进入老年代
大对象指:需要大量占用连续内存空间的对象、在java中一般指:很长的字符串、很长的数
组。
遇到“朝生夕灭”的“短命大对象”是最让jvm难受的一件事,所以可以让大对象直接进入老年
代,这样做的目的是避免“大对象”在Eden区、和Survivor区之间发生大量的内存复制。
虚拟机提供了一个 -XX:PretenureSizeThreshold=3145728,的参数使超过这个值“大对象”
直接在“老年代”中分配。
6.1.java堆内存结构图
7.java方法区。
线程共享。
java虚拟机规范,把“方法区” 描述为 “java堆”的一个逻辑部分。
存放,加载的类信息,常量,静态变量,及时编译后的代码
有人也把java方法区,称为 ”永久代“,也就是数据永久存在。
8.运行时常量池。
运行时常量池 ,是,方法区的,一部分。
运行时常量池,用于存放编译期间解析,的各种“字面量”和“符号引用”,还有类的版本,字段,方法名,接口,等。
常量,并不一定只有编译期间才产生,运行期间也可以将新的常量放入运行时常量池。String类的intern()方法就是如此。
9,直接内存。
就是cup直接提供的物理内存。
jdk1.8有个“元空间”的概念,“元空间”就是jvm中”方法区“的具体实现,而在jdk1.8以前,方法区又被称作”永久代“。
这么说,而在jdk1.8以前,方法区中存放的是,类信息、静态变量、常量,这些信息理论上来说
是不可以被垃圾回收的,所有称“方法区”为“永久代”,他占用的内存空间是java堆中的内存空间。
jdk1.8后“元空间”,占用的内存空间是向cup申请的。
10.对象的内存分布。
三部分:
对象头、实例数据、对齐填充:
对象头第一部分信息:
用于存储对象自身运行时的数据,如hashCode、GC分代年龄、锁状态,线程持有锁
对象头第二部分信息:
存储 类型指针,就是对象指向他的类的元数据的指针,虚拟机通过这个指针来确定对象是哪
个类的实例。
实例数据:
就是对象真正存储的有效信息,
对齐填充:
仅仅起到占位符的作用,
HotSpot VM的自动内存管系统,要求对象起始地址大小必须是8字节的整倍数,就是说对象
的大小必须是8字节的整倍数,因此当对象实例数据没有对齐时(也就是没有满足8字节的整
数倍时),就需要对齐填充来补全。
11.对象访问定位符。
使用句柄、直接指针。
句柄:
java会从堆中会划分一块内存作为“句柄池”,jvm栈中“reference”存放的是对象句柄的地址,
而句柄中。
包含了对象“实例数据”、“类型数据”的地址信息。
优势:reference中储存的句柄地址是稳定的,在对象被移动时(在垃圾回收时对象移动是非
常普遍的),只要修改句柄中的实例数据的指针,而reference本身不需要修改。
直接指针:
jvm栈“reference”中直接存放的是对象地址。
优势:速度快,它节省了一次指针定位的开销。
14.jvm如何调优
尽可以能,少执行GC
让“大对象”直接进入老年代
15.Minor GC 、 和 Full GC
Minor GC:在新生带中发生“垃圾回收”动作,因为大多数java对象具有“朝生夕灭”的特性,所有
Minor GC发生非常频繁,回收速度也比较快。
Full GC:在“老年代”中发生的GC,比Minor GC要慢10倍。