java虚拟机基础教程(Java基础整理篇--JVM(入门必看))java基础 / Java虚拟机基础...

wufei123 发布于 2024-06-28 阅读(7)

1)基本概念JVM 是可运行 Java 代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域JVM是运行在操作系统之上的,它与硬件没有直接的交互Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。

Java语言的可移植性正是建立在Java虚拟机的基础上任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行这就是“一次编译,多次运行”2)java代码的执行使用javac 命令,通过编译器将java源文件编译为.class字节码文件文件;字节码文件通过解释器,编译成特定机器上的机器码。

java虚拟机基础教程(Java基础整理篇--JVM(入门必看))java基础 / Java虚拟机基础...

Java源文件-->编译器-->字节码文件字节码文件-->JVM-->机器码每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是 Java 为什么能够跨平台的原因了3)内存管理方法区堆方法栈PC寄存器

4)JVM内存分类私有区域:生命周期与线程生命周期相同程序计数器:一块较小的内存空间,当前线程所执行的字节码的行号指示器,每条线程都需要独立的程序计数器虚拟机栈:java方法执行的内存模型,每个方法执行的同事都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

方法调用-->执行完成==入栈-->出栈本地方法区:和Java Stack作用类似,虚拟机栈为执行Java方法服务,而本地方法栈则为Native方法服务线程共享区域:随虚拟机的启动/关闭,而创建/销毁JAVA堆:线程共享的内存区域,创建的对象和数组保存在堆内存中,是GC进行垃圾收集的最重要内存区域。

VM采用分带收集算法,所以java堆还可以分为,新生代和老年代方法区:也称永久代,用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码,使用Java堆的永久代来实现方法区运行时常量池是方法区的一部分。

5)JVM运行时内存(从GC角度)新生代:占用1/3堆空间,用于存放新生的对象,由于频繁创建对象,所以会出发MinorGC进行垃圾回收Eden区:Java新对象的出生地(如果占用内存大,直接分配到老年代),Eden区不够时会出发MinorGC,对新生代区进行一次垃圾回收。

ServivorForm:上一次GC的幸存者,作为这一次GC的被扫描者ServivorTo:保留了一次MinorGC过程中的幸存者老生代:占用2/3堆空间,主要存放应用程序中生命周期长的内存对象其中的对象比较稳定,不会被MajorGC频繁执行。

6)MinorGC的过程采用复制算法复制->清空->互关1.将Eden和from中存活的对象复制到to区域2.清空Eden和from中的对象3.to和from中的对象互换7)MajorGC采用标记清除算法,因为要扫描再回收,所以耗时比较长。

会产生内存碎片,为减少内存损耗,一般进行合并或者标记出来方便下次直接分配,装满了会抛出OOM(Out of Memory)异常扫描所有老生代,标记处存活的对象回收没有标记的对象8)永久代(java8中已经被移除

)指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,class在被加载的时候放入永久区域,他和存放实例的区域不用,GC不会在主程序运行期对永久区域进行清理,所以会随着加载class增多而胀满,最终抛出OOM。

java8中被移除,“元数据区”(元空间)取而代之元空间和永久代类似,区别在于:元空间不在虚拟机中,二十使用本地内存9)GC如何确认垃圾引用计数法:在Java中,引用和对象是有关联的,要操作对象必须用引用进行。

所以可以通过引用计数来判断对象是否可以回收可达性分析:通过一系列的“GC roots”对象作为起点搜索如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的但是不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。

两次标记后仍然是可回收对象,则将面临回收10)GC垃圾清除算法标记清除算法(Mark-Sweep):最基础的回收算法,分为标记和清除标记阶段标记处所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。

复制算法(copying):为了解决标记清楚算法内存碎片化缺陷而被提出的算法按内存容量将内存划分为等大小的两块每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上,把已使用的内存清掉标记整理算法

(Mark-Compact):标记阶段和Mark-Sweep算法相同,标记后不是清除对象,而是将存活对象移向内存的另一端,然后清除端边界外的对象分代收集算法:目前大部分JVM所采用的方法,核心思想是根据对象存活的不同生命周期将内存划分为不同的域。

老生代与新生代新生代使用复制算法、标记-清除,前边已经介绍过老生代使用标记整理算法11)JAVA中四种引用类型强引用:最常用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用当一个对象被强引用变量引用时,它处于可达状态,它不可能被垃圾回收机制回收,即使该对象以后永远都不会被用到JVM也不会回收。

因此强引用是造成Java内存泄露的主要原因之一软引用:使用SoftReference类来实现,当系统内存足够时他不会被回收,当系统内存空间不足时他会被回收通常用在内存敏感的程序中弱引用:使用WeekReference类实现,比软引用生存期更短。

只要垃圾回收机制一运行,不管JVM内存空间是否足够,总会回收该对象占用的内存虚引用;使用PhantomReference类实现,他不能单独使用,必须和引用队列联合使用虚引用主要作用是跟踪对象被垃圾回收的状态。

12)GC垃圾收集器Serial(单线程+复制算法):最基本的垃圾收集器,使用复制算法单线程收集器,不但只会使用一个CPU或一条线程去完成垃圾收集工作,并且在垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。

虽然需要暂停其他工作线程,但是简单高效,对于先定单个CPU环境来说,没有线程交互的开销,可以获得更高的单线程垃圾收集效率,因此Serial垃圾收集器依然是Java虚拟机运行在Client模式下默认的新生代垃圾收集器。

PerNew(Serial+多线程):是Serial收集器的多线程版本,也是使用了复制算法PerNew收集器默认开启和CPU数目相同的线程数,可以通过-xx:ParallelGCThreads参数来闲置垃圾收集器的线程数【Parallel:平行的】,ParNew垃圾收集器是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

Parallel Scavenge收集器(多线程复制算法、高效):重点关注的是程序达到一个可控制的吞吐量,具有自适应提哦啊接策略Serial Old(单线程标记整理算法):是Serial收集器年老代版本。

Parallerl Old (多线程标记整理算法):是Parallel Scavenge的年老代版本,使用多线程的标记-整理算法CMS收集器(多线程标记清除算法):是年老代垃圾收集器,主要目标是获取最短垃圾回收停顿时间,分为四个阶段。

初始标记并发标记重新标记并发清除G1收集器:Garbage first垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比于CMS收集器,G1两个最突出的改进是:基于标记-整理算法,不产生内存碎片可以非常精确的控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

G1收集器避免全区域垃圾收集,它吧堆内存划分为大小固定的几个独立区域,优先回收垃圾最多的区域13)JAVA IO/NIO阻塞IO模型:最传统的一种IO模型,在读写数据过程中会发生阻塞现象当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态。

典型的阻塞IO模型的例子为:data = socket.read();如果没有就绪,就会一直阻塞在read方法非阻塞IO模型:当用户线程发起一个read操作后,并不需要等待,而是马上就得到一个结果如果结果是一个error时,他就知道数据还没有准备好,于是它可以再次发送read操作。

在此模型下,用户线程需要不断地询问内核数据是否就绪多路复用IO模型:目前使用比较多的模型Java NIO实际上就是多路复用IO在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。

信号驱动IO模型:当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号后,便在信号函数中调用IO读写操作来进行实际IO请求操作。

异步IO模型:最理想的IO模型,当用户线程发起read操作之后,立刻就可以开始做其他的事情然后内核会等待数据准备完成,将数据拷贝到用户线程,当一切的完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。

也就是说用户线程完全不需要知道实际的整个IO操作是如何进行的,只需要发起一个请求,当接收到内核返回成功信号表示IO操作已经完成,可以直接去使用数据了14)JAVA NIONIO主要由三大核心部分:Channel(通道),Buffer(缓冲区),Selector。

传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer进行操作,数据总是冲通道读取到缓冲区中,或者从缓冲区写到同道中Selector(选择区)用于监听多个通道的时间(比如:连接打开,数据到达)。

因此,单个线程可以监听多个数据通道NIO和传统IO之间第一个最大的 区别是,IO是面向流的,NIO是面向缓冲区的15)NIO的缓冲区数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区前后移动增加了处理过程汇总的灵活性,但是需要检查是否该缓冲区中包含所有您需要处理的数据。

而且,须确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据16)JVM类加载机制JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化加载:这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。

不一定要从一个Class文件获取,这里既可以从ZIP包中获取(比如jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其他文件生成(比如将JSP文件转换成对应的Class类)验证:这一阶段主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备:准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间解析:指虚拟机将常量池中的符号引用替换为直接引用的过程符号引用:符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。

直接引用:直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄初始化:类加载最后的一个阶段,前边的类加载阶段之后,除了在加载阶段可以自定义类加载器外,其他操作都由JVM主导到了初始阶段,才开始真正执行类中定义的Java程序代码。

17)双亲委派当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是吧这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

感谢阅读,如果觉得本文对你有帮助,记得点个赞再走哦~

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻40348