java进阶之阶段一
2020-12-24 12:27
标签:ade 内存管理机制 独立 证明 条件 手机系统 debugger 基础 boolean ? JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。 ? javac – 编译器,将源程序转成字节码 ? jar – 打包工具,将相关的类文件打包成一个文件 ? javadoc – 文档生成器,从源码注释中提取文档 ? jdb – debugger,查错工具 ? java – 运行编译后的java程序(.class后缀的) ? appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器。 ? Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件。 ? Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。 ? Jconsole: Java进行系统调试和监控的工具 ? 是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。 ? ? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。 ? 1,类装载器(ClassLoader)主要负责加载class文件,是否能执行主要取决于execution engine它是负责执行被加载类中包含的指令。有两种类加载器分别为启动类加载器和用户自定义类加载器,然而启动类加载器是JVM实现的一部分,用户自定义类加载器是Java程序一部分。 ? 2,本地方法栈(native method stack)主要作用是登记native方法,然后在execution engine执行的时候加载本地方法库。 ? 3,栈有时我们又叫栈内存,负责Java程序的运行,它是在线程创建时创建的,所以生命周期也是和线程生命周期一致,同时消亡,线程结束了栈也就释放,特别提醒的是栈不存在垃圾回收的问题,因为线程结束栈就是释放了。平时我们写的类变量、引用类型变量、实例方法等等都是在函数的栈内存分配好。 ? 4,程序计数器,是指方法区中的方法字节码由引擎读取下一条指令,它是一个非常小的内存空间。为什么有这种东西呢,大家都知道每个线程都是有一个程序计数器的,是线程私有的,相当一个指针。 ? 5,方法区它是指线程共享的,谁都可以共享使用,我们通常用来保存装载类的元结构信息。 ? 6,堆(heap)它是Java虚拟机用来存储对象实例的,比我们在开发过程使用的new对象,只要通过new创建的对象的内存的对象都在堆分配,注意一点的是堆中的对象内存需要等待垃圾器(GC)进行回收,也是Java虚拟机共享区。 ? 7,本地接口(native interface)作用是融合不同的编程语言为Java所用,注意底层是C、C++写的,学习JVM时了解C语言一些更好,最起码能看懂,这个方法的行为就是native method stack中登记native方法,然后在execution engine执行时加载native libraries的。 第一块:PC寄存器 PC寄存器是用于存储每个线程下一步将执行的JVM指令,如该方法为native的,则PC寄存器中不存储任何信息。 第二块:JVM栈 JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址。 第三块:堆(Heap) 它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。 (1) 堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的 (2) Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配 (3) TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。 (4) 所有新创建的Object 都将会存储在新生代Yong Generation中。如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。新的Object总是创建在Eden Space。 第四块:方法区域(Method Area) (1)在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代。 (2)方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。 第五块:运行时常量池(Runtime Constant Pool) 存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。 第六块:本地方法堆栈(Native Method Stacks) JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。 ? java.util.concurrent(简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现等。 ? JSE 指标准版一般用于用户学习JAVA语言的基础也是使用其他两个版本的基础主要用于编写C/S项目和提供标准的JAVA类库,是所有基于Java语言开发的基础,该版本主要用于开发桌面应用程序。 ? JEE 指企业版依托互连网技术提供企业级平台应用说白了就是用来构建大型网站和B/S系统 ,作为一个企业版本,主要是给出一个开发企业级应用架构的解决方案,同时给出了在这个架构中相关组件以供开发人员使用,例如我们连接数据库所用的JDBC。 ? JME 指移动版为小型移动器械搭建使用平台主要是用来为手机编程,制作手机相关软件的 三个版本一个是做C/S项目如QQ 一个是做网站如163 一个是做手机系统如大部分手机的小游戏 ,是针对移动设备,嵌入式系统的开发。 java程序执行过程分为两步,下图为流程示意图 ? 第一步:将java源码(.java文件)通过编译器(javac.exe)编译成JVM文件(.class文件) ? 第二步:将JVM文件通过java.exe执行,输出结果 通过如上分析,我们发现JVM至关重要,其向上屏蔽了操作系统的差异,也正因为JVM的该作用,才使java这门编程语言能够实现跨平台, 其原理大致可描述为如下: 理解GC机制就从:“GC的区域在哪里”,“GC的对象是什么”,“GC的时机是什么”,“GC做了哪些事”几方面来分析。 ? GC (Garbage Collection, 垃圾回收) 是指一种自动的内存管理机制. 在这种机制中由垃圾回收器来负责回收用户进程中不再使用的对象所占据的内存. ? 内存运行时JVM会有一个运行时数据区来管理内存。它主要包括5大部分:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap).而其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡。例如栈中每一个栈帧中分配多少内存基本上在类结构去诶是哪个下来时就已知了,因此这3个区域的内存分配和回收都是确定的,无需考虑内存回收的问题。但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道会创建哪些对象,这部分内存的分配和回收都是动态的,GC主要关注的是这部分内存。 GC主要进行回收的内存是JVM中的方法区和堆; 由于程序计数器、Java虚拟机栈、本地方法栈都是线程独享,其占用的内存也是随线程生而生、随线程结束而回收。而Java堆和方法区则不同,线程共享,是GC的所关注的部分。 在堆中几乎存在着所有对象,GC之前需要考虑哪些对象还活着不能回收,哪些对象已经死去可以回收。 有两种算法可以判定对象是否存活: 1.)引用计数算法:给对象中添加一个引用计数器,每当一个地方应用了对象,计数器加1;当引用失效,计数器减1;当计数器为0表示该对象已死、可回收。但是它很难解决两个对象之间相互循环引用的情况。 2.)可达性分析算法:通过一系列称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(即对象到GC Roots不可达),则证明此对象已死、可回收。Java中可以作为GC Roots的对象包括:虚拟机栈中引用的对象、本地方法栈中Native方法引用的对象、方法区静态属性引用的对象、方法区常量引用的对象。 在主流的商用程序语言(如我们的Java)的主流实现中,都是通过可达性分析算法来判定对象是否存活的。 无论是引用计数器还是可达性分析,判定对象是否存活都与引用有关!那么,如何定义对象的引用呢? 当内存空间还够时,能够保存在内存中;如果进行了垃圾回收之后内存空间仍旧非常紧张,则可以抛弃这些对象。所以根据不同的需求,给出如下四种引用,根据引用类型的不同,GC回收时也会有不同的操作: 关于方法区中需要回收的是一些废弃的常量和无用的类。 最基础的算法,分标记和清除两个阶段:首先标记处所需要回收的对象,在标记完成后统一回收所有被标记的对象。 它有两点不足:一个效率问题,标记和清除过程都效率不高;一个是空间问题,标记清除之后会产生大量不连续的内存碎片(类似于我们电脑的磁盘碎片),空间碎片太多导致需要分配大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。 为了解决效率问题,出现了“复制”算法,他将可用内存按容量划分为大小相等的两块,每次只需要使用其中一块。当一块内存用完了,将还存活的对象复制到另一块上面,然后再把刚刚用完的内存空间一次清理掉。这样就解决了内存碎片问题,但是代价就是可以用内容就缩小为原来的一半。 复制算法在对象存活率较高时就会进行频繁的复制操作,效率将降低。因此又有了标记-整理算法,标记过程同标记-清除算法,但是在后续步骤不是直接对对象进行清理,而是让所有存活的对象都向一侧移动,然后直接清理掉端边界以外的内存。 当前商业虚拟机的GC都是采用分代收集算法,这种算法并没有什么新的思想,而是根据对象存活周期的不同将堆分为:新生代和老年代,方法区称为永久代(在新的版本中已经将永久代废弃,引入了元空间的概念,永久代使用的是JVM内存而元空间直接使用物理内存)。 这样就可以根据各个年代的特点采用不同的收集算法。 新生代中的对象“朝生夕死”,每次GC时都会有大量对象死去,少量存活,使用复制算法。新生代又分为Eden区和Survivor区(Survivor from、Survivor to),大小比例默认为8:1:1。 老年代中的对象因为对象存活率高、没有额外空间进行分配担保,就使用标记-清除或标记-整理算法。 新产生的对象优先进去Eden区,当Eden区满了之后再使用Survivor from,当Survivor from 也满了之后就进行Minor GC(新生代GC),将Eden和Survivor from中存活的对象copy进入Survivor to,然后清空Eden和Survivor from,这个时候原来的Survivor from成了新的Survivor to,原来的Survivor to成了新的Survivor from。复制的时候,如果Survivor to 无法容纳全部存活的对象,则根据老年代的分配担保(类似于银行的贷款担保)将对象copy进去老年代,如果老年代也无法容纳,则进行Full GC(老年代GC)。 大对象直接进入老年代:JVM中有个参数配置-XX:PretenureSizeThreshold,令大于这个设置值的对象直接进入老年代,目的是为了避免在Eden和Survivor区之间发生大量的内存复制。 长期存活的对象进入老年代:JVM给每个对象定义一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳,将被移入Survivor并且年龄设定为1。每熬过一次Minor GC,年龄就加1,当他的年龄到一定程度(默认为15岁,可以通过XX:MaxTenuringThreshold来设定),就会移入老年代。但是JVM并不是永远要求年龄必须达到最大年龄才会晋升老年代,如果Survivor 空间中相同年龄(如年龄为x)所有对象大小的总和大于Survivor的一半,年龄大于等于x的所有对象直接进入老年代,无需等到最大年龄要求。 单线程收集器,表示在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。"Stop The World". 实际就是Serial收集器的多线程版本。 该收集器比较关注吞吐量(Throughout)(CPU用于用户代码的时间与CPU总消耗时间的比值),保证吞吐量在一个可控的范围内。 CMS收集器是一种以获得最短停顿时间为目标的收集器。 从JDK1.7 Update 14之后的HotSpot虚拟机正式提供了商用的G1收集器,与其他收集器相比,它具有如下优点:并行与并发;分代收集;空间整合;可预测的停顿等。 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(loading), 验证(verification), 准备(preparation), 解析(resolution), 初始化(initialization), 使用(using), 卸载(unloading). JVM在此阶段需要完成: 该阶段的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。直接决定了JVM能否承受恶意代码的攻击。 准备阶段正式为类变量分配内存并设置类变量(不包含实例变量)初始值,这些变量所使用的内存都将在方法区中进行分配。 注意:类变量value的值在准备阶段后的初始值为0而不是123,把value赋值为123的动作在初始化阶段才会被执行。 解析阶段是JVM将常量池内的符号引用替换为直接引用的过程。 假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,则JVM会完成3个步骤: 要解析一个未被解析过得字段符号引用,则首先会解析字段所属的类或接口的符号引用,设为C,然后JVM会从这个类开始逐级往父类或接口找与这个字段相匹配的字段: 其中,方法解析分为类方法解析与接口方法解析,两者略有不同。 接口方法也要首先解析出该方法所属的类或接口,假设为C: 初始化时类加载过程的最后一步,在这个阶段,才真正开始执行类中定义的Java程序代码。 从Java虚拟机的角度看,只有两种不同的类加载器: 从Java开发人员看,类加载器可分为3种: 我们的应用程序都是由着3种类加载器互相配合进行加载的,他们的关系如图所示。而这种层次关系称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型中,类加载器之间的父子关系一般不会以继承(Inheritance)而是以组合(Composition)的关系来复用父类加载器。 双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求。它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的加载器都是如此。所以所有的加载请求都会传送到顶层的启动类加载器中加载,只有当父类加载器表示自己无法加载该类时,才会由子加载器尝试加载。 这样做的优势也很直观:因为我们前面有提到如果两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,则这两个类就不相等。为了避免同一个类被识别成不同类,所以应该使用双亲委派模型。 java进阶之阶段一 标签:ade 内存管理机制 独立 证明 条件 手机系统 debugger 基础 boolean 原文地址:https://www.cnblogs.com/caozhenghua/p/13210384.html1.几个常用单词
1.1 jdk
jdk1.8新特性
JDK包含的基本组件包括:
1.2JRE
1.3 JVM
什么是jvm?
JVM的实现原理
JVM运行时数据区
1.4 JUC
1.5 Java SE
1.6 Java EE
1.7 JME
2.java运行流程
3.GC
3.1 什么是GC?
为什么要有GC?
哪些内存需要回收?
涉及到多线程(指堆)、多个对该对象不同类型的引用(指方法区),才会涉及GC的回收。什么时候回收?
对象“已死”的判定算法
3.2 GC常用算法
标记-清除算法
复制算法
标记-整理算法
分代收集算法
3.3 垃圾收集器
Serial收集器
ParNew收集器
Parallel Scavenge收集器
CMS(Concurrent Mark Sweep)收集器
G1(Garbage First)收集器
4.类加载器
简而言之,JVM把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。加载(Loading)
验证(Verification)
准备(Preparation)
public static int value = 123;
解析(Resolution)
需要进行类或接口的解析,字段解析,方法解析。类或接口的解析
字段解析
类方法解析
对于类方法解析,同字段解析一样,首先要解析出该类方法所属的类或接口的符号引用,假设解析为C:
接口方法解析
初始化(Initialization)
在Preparation阶段,已经给类变量有过一次初始的赋0或null的设置,是执行类构造器clinit()方法的过程。
这里稍微列一下clinit()方法的特点:
双亲委派模型
上一篇:JS中字符串和数组的相互转化
下一篇:什么是多线程中的上下文切换?