首页 > 编程语言 >java基础知识三

java基础知识三

时间:2023-09-09 10:04:23浏览次数:42  
标签:java 标记 引用 收集器 回收 基础知识 内存 加载

1.Java程序运行机制详细说明

首先利用IDE集成开发工具编写Java源代码,源文件的后缀为.java;

再利用编译器(javac命令)将源代码编译成字节码文件,字节码文件的后缀名为.class;

运行字节码的工作是由解释器(java命令)来完成的。

java文件通过编译器变成了.class文件,接下来类加载器又将这些.class文件加载到JVM中。

其实可以一句话来解释:类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。


2.JVM运行时数据区

1、堆

JVM中最大的一块,主要用来存放对象实例和数组,几乎所有的对象实例都在这里分配内存。线程共享,内部会划分出多个线程私有的分配缓冲区(TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。

2、虚拟机栈

每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。线程私有,生命周期和线程一致。

3、方法区(非堆)

属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

4、本地方法栈

本地方法栈(Native MethodStacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。

5、程序计数器

程序计数器(Program CounterRegister)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。此内存区域是唯一一个在Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域


3.说说堆和栈的区别

1)物理地址

堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,

所以有各种算法。比如,标记-消除,复制,标记-整理,分代(即新生代使用复制算法,老年

代使用标记——压缩)

栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。

2)内存分配

堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大

于栈。

栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。

3)存放的内容

堆存放的是对象的实例和数组。因此该区更关注的是数据的存储

栈存放:局部变量,操作数栈,返回结果。栈区更关注的是程序方法的执行。

4)程序的可见度

堆对于整个应用程序都是共享、可见的。

栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。


4.引用类型

对象引用类型分:强引用、软引用、弱引用和虚引用。

强引用:不会被回收。就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。

软引用:内存充足,则不回收;不足则回收。软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。

弱引用:会被回收。弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。

主流的有三种垃圾回收算法:复制算法,标记-清除算法、标记-整理算法


5.JVM有多种垃圾回收算法,其中目前在用最经典的就是分代收集算法。

永久代(Perm):主要保存class,method,field等对象,该空间大小,取决于系统启动加载类的数量,一般该区域内存溢出均是启动时溢出。java.lang.OutOfMemoryError: PermGen space

老年代(Old):一般是经过多次垃圾回收(GC)没有被回收掉的对象。

伊甸园(Eden):新创建的对象。

幸存区0(Survivor0):经过垃圾回收(GC)后,没有被回收掉的对象。

幸存区1(Survivor1):同Survivor0相同,大小空间也相同,同一时刻Survivor0和Survivor1只有一个在用,一个为空。


6.标记-清除算法

首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。缺点:标记和清除两个过程效率都不高;标记清除后会产生空间碎片,空间碎片导致分配较大对象时可能提前触发垃圾回收。


7.复制算法

将可用内存分为两个区域,每次只使用其中一块,当使用的那一块内存用完时,将还存活的对象复制到另外一块内存中,然后把已使用过的内存空间一次清理掉。优点:解决的空间碎片问题,实现简单。缺点:需要两倍空间,将内存缩小为两块,内存使用率不高。复制操作频繁效率变低。


8.标记-整理算法

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。


9.垃圾收集器CMS G1

垃圾回收策略可以看作是内存回收的抽象策略,而垃圾收集器是内存回收的具体实现

垃圾收集器有很多种,常见的有:串行收集器、并行收集器、并发收集器、CMS收集器以及最新的G1收集器。重点为CMS收集器和G1收集器


10.CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器

基于 标记清除 算法实现。第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

特点:

针对老年代

基于"标记-清除"算法(不进行压缩操作,会产生内存碎片)

以获取最短回收停顿时间为目标

并发收集、低停顿

需要更多的内存

运作步骤:

初始标记: 暂停所有的其他线程,标记GC Roots能直接关联到的对象,速度很快;

并发标记:进行GC Roots Tracing的过程;

重新标记: 修正并发标记期间的变动部分,需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;

并发清除: 开启用户线程,同时GC线程开始对为标记的区域做清扫,回收所有的垃圾对象。

缺点:

对 CPU 资源敏感;

无法收集浮动垃圾;

标记清除 算法带来的空间碎片。


11.G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器。以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。

G1是将整个堆空间分成许多个大小不等的独立区域(Region),大约有2000块,每个Region从1M到32M大小不等,在JVM启动的时候就已经分割好了,Region可采用并行的垃圾回收或 NOT STW 方式。

运作步骤:

初始标记(Initial Marking)

并发标记(Concurrent Marking)

最终标记(Final Marking)

筛选回收(Live Data Counting and Evacuation)

特点:


1、并行与并发

G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。

2、分代收集

虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。

能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;

能够采用不同方式处理不同时期的对象;

虽然保留分代概念,但Java堆的内存布局有很大差别;

将整个堆划分为多个大小相等的独立区域(Region);

新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合。

3、空间整合

与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。是一种类似火车算法的实现,不会产生内存碎片,有利于长时间运行。

4、可预测停顿

这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型。可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒。在低停顿的同时实现高吞吐量。

为什么G1可以实现可预测停顿?

可以有计划地避免在Java堆的进行全区域的垃圾收集;

G1收集器将内存分大小相等的独立区域(Region),新生代和老年代概念保留,但是已经不再物理隔离。

G1跟踪各个Region获得其收集价值大小,在后台维护一个优先列表;

每次根据允许的收集时间,优先回收价值最大的Region(名称Garbage-First的由来);

12.类加载器,加载类的流程

类加载器实现的功能是:为加载阶段获取二进制字节流

1、加载

加载主要是将.class文件(并不一定是.class。可以是ZIP包,网络中获取)中的二进制字节流读入到JVM中。

在加载阶段,JVM需要完成3件事:

通过类的全限定名获取该类的二进制字节流;

将字节流所代表的静态存储结构转化为方法区的运行时数据结构;

在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2、连接

2.1 验证

验证阶段,确保加载进来的字节流符合JVM规范。将完成以下4个阶段的检验动作:

文件格式验证

元数据验证(是否符合Java语言规范)

字节码验证(确定程序语义合法,符合逻辑)

符号引用验证(确保下一步的解析能正常执行)

2.2 准备

准备阶段,为静态变量在方法区分配内存,并设置默认初始值。

2.3 解析

解析阶段,是虚拟机将常量池内的符号引用替换为直接引用的过程。

3、初始化

初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。

(注:当有父类且父类为初始化的时候,先去初始化父类;再进行子类初始化语句)

类加载器之间的这种层次关系叫做双亲委派模型。


13.双亲委派模型

双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。


14.双亲委派模型的工作过程

如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器(Bootstrap ClassLoader)。

只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。


15.为何要双亲委派机制(避免同一个类加载出不同的结果使得程序混乱)

为何要采用双亲委派机制呢?了解为何之前,我们先来说明一个知识点:判断两个类相同的前提是这两个类都是同一个加载器进行加载的,如果使用不同的类加载器进行加载同一个类,也会有不同的结果。

如果没有双亲委派机制,会出现什么样的结果呢?比如我们在rt.jar中随便找一个类,如java.util.HashMap,那么我们同样也可以写一个一样的类,也叫java.util.HashMap存放在我们自己的路径下(ClassPath).那样这两个相同的类采用的是不同的类加载器,系统中就会出现两个不同的HashMap类,这样引用程序就会出现一片混乱。

标签:java,标记,引用,收集器,回收,基础知识,内存,加载
From: https://blog.51cto.com/u_16243779/7417341

相关文章

  • 无涯教程-JavaScript - IMLOG2函数
    描述IMLOG2函数以x+yi或x+yj文本格式返回复数的以2为底的对数。可以从自然对数计算复数的以2为底的对数,如下所示-$$\log_2(x+yi)=(log_2e)\ln(x+yi)$$语法IMLOG2(inumber)争论Argument描述Required/OptionalInumberAcomplexnumberforwhichyouwantth......
  • Java(day08):变量、常量及其作用域
    前言Java是一种面向对象编程语言,它通过变量和常量来处理数据类型。本文将介绍Java中的变量、常量及其作用域以及如何使用它们。摘要本文将介绍Java中的变量、常量及其作用域。我们将讨论变量和常量的含义、如何定义和使用它们,以及作用域的概念和限制。我们还将提供代码示例......
  • Java从入门到精通-数组(二)
    4.数组的基本操作数组的基本操作包括遍历数组、填充替换数组元素、对数组进行排序、复制数组以及查询数组中的元素。•4.1遍历数组遍历数组是访问数组中所有元素的过程,通常使用循环完成。使用 for 循环遍历数组:int[]numbers={1,2,3,4,5};for(inti=0;i<numbers.l......
  • 简单梳理java中的类加载
    一、类加载器简介java中自带的类加载器可以分为根类加载器(BootStrapclassloader),扩展类加载器,应用类加载器,这三个都不是用java语言实现的。其中根类加载器和扩展类加载器用来加载java自带的一些类,而应用类加载器用来加载我们自己写的java类编译后的class文件,也就是classpath......
  • Java基础知识
    一、基础知识注释的作用解释说明程序,提高程序的阅读性帮助我们调试程序Java语言最基本的单位的类,所以我们首先要定义一个类Java程序要想能够独立运行,必须要有主方法如果想要Java程序有输出,必须要有输出语句定义类的格式:classclassname{......
  • java8学习
    java8安装与环境变量配置chocochoco官网安装命令:chocoinstalljdk8自动配置环境变量IDEA官网安装java语法注:由于默认具有cpp基础,所以和cpp极为相似的点我不会提及。enum枚举publicenumPlayerType{TENNIS("网球"),FOOTBALL("足球"),//常量FOOTBALL......
  • 使用 idea debug 远程 java 进程
    线上环境使用的jdk版本为1.8,对应的java启动命令java-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50050-jarxxxx.jar注意服务器需要开放对应的50050tcp端口idea配置:Run->EditConfiguration->+->RemoteJVMDebug->填写ip端口->启......
  • Java学习_004 数据输入:案例2
    需求:三个和尚的身高需要手动输入,请用程序实现这三个和尚的最高身高。importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intheight1=sc.nextInt();intheight2......
  • Java学习_003 数据输入
    1.数据输入1.2Scanner使用的基本步骤(1)导包importjava.util.Scanner;(2)创建对象Scannersc=newScanner(System.in);(3)接受数据inti=sc.nextInt();1.3实例importjava.util.Scanner;publicclassMain{pub......
  • Java学习_002 案例2:三个和尚
    需求:一座寺庙里住着三个和尚,已知他们的身高分别为150cm、210cm、165cm,请用程序实现获取这三个和尚的最高身高。1publicclassMain{2publicstaticvoidmain(String[]args){3intheight1=150;4intheight2=210;5intheight3......