当前位置: 首页 > news >正文

海安县住房和城乡建设局网站图片制作二维码

海安县住房和城乡建设局网站,图片制作二维码,买一台服务器需要多少钱,2021网页设计尺寸一)JVM是如何运行的#xff1f; 1)在程序运行前先将JAVA代码转化成字节码文件也就是class文件#xff0c;JVM需要通过类加载器将字节码以一定的方式加载到JVM的内存运行时数据区#xff0c;将类的信息打包分块填充在运行时数据区#xff1b; 2)但是字节码文件是JVM的一套指… 一)JVM是如何运行的 1)在程序运行前先将JAVA代码转化成字节码文件也就是class文件JVM需要通过类加载器将字节码以一定的方式加载到JVM的内存运行时数据区将类的信息打包分块填充在运行时数据区 2)但是字节码文件是JVM的一套指令运行规范并不能直接交给底层的操作系统来执行因此需要特殊的命令解析器也就是JVM的执行引擎会将字节码翻译成底层的操作系统指令也就是0 1的二进制操作系统指令数据交给CPU执行因为操作系统只认机器码不认识字节码 3)执行引擎在进行执行的过程中也会调用其它语言的接口比如说调用本地库接口调用本地方法来实现整个程序的运行 JVM类加载器运行时数据区执行引擎本地库接口 1)本地方法接口:简单来说就是一个本地方法就是一个JAVA调用非JAVA代码的一个接口方法体不是由JAVA代码写的设置优先级之类的简单来说一个本地方法就是一个JAVA调用非JAVA代码的接口一个本地方法的方法并不是由JAVA方法实现的比如说C本地接口的作用就是融合不同的编程语言为JAVA所用。本地方法不是抽象方法native方法是存在方法体但是abstract不存在方法体标识符native可以和其它所有的java标识符连用但是不可以和abstract连用 2)因为本身JAVA的实现非常简单但是有些层次的任务使用JAVA实现就非常不容易了或者对于程序执行的执行效率很在意的时候问题就出现了 2.1)需要和JAVA外部的环境进行交互:有的时候JAVA应用需要和外部的环境进行交互这是本地方法存在的主要的原因JAVA需要和底层的操作系统交互系统或者是某一些硬件交换信息的时候本地方法提供了一些交互机制 1)类加载器:加载class字节玛的内容到内存中 2)运行时数据区:负责管理JVM使用到的内存比如说创建对象和销毁对象 3)方法区:常量域信息只有HotSpot虚拟机才有 4)执行引擎:将字节码文件中的内容解析成机器码同时使用即时编译优化性能 5)本地方法接口:调用本地已经编译的方法比如说虚拟机中已经提供好的C/C的方法 6)翻译字节码:针对于字节码的指令进行解释执行 7)JIT编译器:针对于热点代码进行二次编译(将字节码中的字节码指令编译成机器指令)并将其缓存起来缓存在方法区中 字符串常量池的底层实现是依靠C的map来实现的C的hashmap也是需要存放局部变量的存放C本地方法的方法调用和局部变量 JMM:一种内存模型为了提升CPU的读写效率充分利用CPU资源 二)类加载子系统类加载的过程类的生命周期 init方法:成员变量真正进行赋值之前是0并且调用对象的构造方法 双亲委派模型 1)沙箱安全机制:自己写的类不会加载这样便可以防止JAVA的核心API不会被修改 2)避免类的重复加载:当父亲已经加载之后子类就没有必要再加载一次 类加载子系统的作用:是一种JAVA虚拟机提供给应用程序去实现获取类和接口字节码的技术类加载器只是参与加载过程中的字节码获取并加载到内存的这一部分 类的生命周期描述了一个类加载使用卸载的过程加载---链接---初始化----使用----卸载 一)loadding: a)根据包的全限定包名类名通过不同的渠道来找到对应的.class文件加载到内存中 b)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构就是将字节码的信息存放到方法区里面方法区就是用来存放已被加载的类信息常量静态变量 c)在方法区中生成这个类的类对象作为方法区中这个类的各种数据的访问入口 二)链接: 2.1)验证:class文件是以特定的文件符开始的校验内容是否满足JAVA虚拟机规范 a)文件格式验证:验证文件是否已特定字符开头就是以特定的二进制文件开头 b)原信息校验:就是对一些基本信息进行校验比如说类必须有父类 c)验证程序执行的语义:比如说方法中的指令执行中跳转到不正确的位置 d)符号引用验证:例如说有没有类中访问private修饰的方法 e)版本号检测:如果返回值是true代表验证成功就是检测JDK的版本 2.2)准备:为静态变量分配内存并设置初始化值为0值是默认值的初值就是防止程序员写出脑残代码比如说给一个a没有赋初值如果程序员进行后续操作打印a final修饰的静态变量在准备阶段就直接复制初始值了因为在编译期的时候直接就可以确定值不会针对于实例变量进行初始化实例变量会随着对象一起被分配到JAVA的堆里面 下面是准备阶段下面分别是两个变量在内存中的状态 2.3)解析:解析所作的操作就是将常量池中的符号引用替换成直接引用 符号引用就是在字节码文件中使用编号来访问字符串常量池中的内容而直接引用不再使用编号而是使用内存地址来直接访问具体的数据 3)初始化阶段:执行静态代码块中的代码并且会给静态变量赋初值 其实本质上初始化就是在执行字节码部分的中的clinit部分的字节码指令 java虚拟机针对于class文件采用的是按需加载的方式也就是说当需要使用该类的时候才会将他的class文件加载到内存中生成类对象而且家在某一个类的class文件的时候JAVA虚拟机采用的是双亲委派模型会把请求交给父亲来处理是一种任务委派模式 在类加载中使用synchronized加锁向上委托检查向下加载 1)避免类的重复加载 2)保护程序安全防止核心API被随意篡改 在JVM中表示两个Class对象是否是同一个需要满足两个条件 1)两个类的完整类名必须完全一致全限定包名和类名 2)加载这个类的classloader实例对象必须相同 换句话说在JVM中即使这两个类对象Class对象来源于同一个Class文件即使一个虚拟机所加载但是只要加载他们的ClassLoader实例对象不同两个类对象也是不相同的 三)字节码文件的组成: 1)基础信息:魔数字节码文件对应的java版本号访问表示public final以及父类和接口  2)常量池:保存了字符串常量类或者是接口名字段名主要在接口中使用 3)字段:当前类或者是接口声明的字段信息 方法:当前类或者接口声明的方法信息字节码指令 属性:指的是类的属性源码的文件名以及类的列表 字节码文件中常量池的作用:避免相同的内容同时定义节省空间不仅会使文件变得非常大况且读取也会非常慢通过常量池节省字节码文件中的一部分空间避免同样的数据出现多次 可以看到字符串的引用存放的是7号的索引点击常量池的索引发现又是一个字面量 最后点击25就可以找到最终的字面量了  iconst:将数字放入到操作数栈中 putstatic:将操作数栈中的数据放到静态变量中 如果将代码进行颠倒clinit字节码指令执行的顺序和java中编写的顺序是一致的 但是为什么字节码文件再进行设计这一块的时候先通过字符串的引用找到字符串再来通过字符串找到字面量呢能不能直接通过字段来找到字面量呢因为JAVA里面的字符串解析并加载中需要将String类型加载到字符串常量池中   从下到上查找是否加载过再从上向下进行记载   下面来看一下哪几种方式会导致类的初始化: 1)当访问到一个类的静态变量或者是静态方法注意变量是final修饰的并且等号右边是常量不会触发初始化 2)调用Class.forName(String className)获取到这个类的类对象的时候 3)new一个该类的对象的时候 4)执行Main方法的当前类 添加-XX:TraceClassLoading参数可以打印出加载并且初始化的类 下面程序的输出结果是: 执行main函数况且类加载只会执行一次所以静态代码块也只会执行一次先进行类加载DACBCB clinit方法在特定的条件下不会出现如下面几种情况是不会执行初始化指令的也就不会生成clinit方法在下面的情况下不会执行初始化操作 1)没有静态代码块况且没有静态变量赋值语句 2)有静态变量的声明但是没有赋值语句public static int a在类加载的准备阶段就会赋值成0 3)静态变量的定义使用final关键字况且这份变量会在准备阶段直接进行初始化 1)直接访问父类的静态变量不会触发到子类的初始化子类的clinit方法执行前会先执行父类的clinit方法 2)声明一个类以后内部至少会存在一个这个类的构造器也就是一定会出现init方法 访问父类的初始化变量只会初始化父类因为a只是在父类中此时打印的是a1 第五步就是为了防止多个线程多次加载同一个类从下面的代码中而可以看到类加载中的静态代码块只会执行一次就相当于是一个加锁的过程一个类在内存中加载一次即可方法区在JDK1.8使用的是元空间会使用直接内存缓存起来了也就是说JAVA虚拟机在执行类加载的时候只会执行一次只会调用一次clinit()方法 非法的前向引用:当一个定义的变量出现在静态代码块之后是可以在静态代码块中赋值的但是是不可以打印这个静态代码块的 四)如何打破双亲委派模型 五) JAVA的运行时数据区 1)网络中的数据和硬盘上面的数据要想能够被CPU运算需要先把数据加载到内存中CPU直接交互的对象就是内存内存充当着磁盘和CPU的桥梁一个JVM实例对应着一个RunTime实例内存是非常重要的系统资源是硬盘和CPU的中间仓库以及桥梁承载着操作系统和应用程序的实时运行JVM的内存布局规定了JAVA在运行过程中内存申请分配和管理的策略保证了JVM的高效稳定运行 2)JAVA虚拟机定义了若干种程序运行期间会使用到的运行时数据区其中有一些随着虚拟机的启动而创建随着虚拟机的退出而销毁另外一些则是和线程是一一对应的这些线程对应的数据区域会随着县城开始和结束而创建或者销毁 3)线程是一个程序中的运行单元JVM允许一个应用有多个线程并行的执行 在Hotspot虚拟机JVM里面每一个线程都和操作系统本地线程直接映射当一个Java线程准备好执行以后此时一个操作系统的本地线程也会创建Java线程终止以后本地线程一会回收操作系统负责所有的线程的安排调度到任何一个可用的CPU上一旦本地线程初始化成功他就会调用Java线程的run()方法 4)如果使用jconsole或者是任何一个调试工具都可以看到在后台有很多线程在运行这些后台线程不包括调用main线程以及所有这个main线程自己所创建的线程 java虚拟机规范白皮书是给JVM开发厂商去看的指导开发厂商实现JVM默认的JVM是hotSpot 1)程序计数器:当前线程执行的字节码指令的地址CPU的个数有限但是任务很多CPU会频繁进行线程切换某一块程序会一直经历执行暂停再继续执行就需要有个东西来记录当前线程执行到哪一步用于存储当前线程执行的执行的字节码的指令的地址在多线程环境下程序计数器用于实现线程切换保证线程恢复执行的时候能够继续从正确的位置开始执行代码 2)JAVA虚拟机栈:用于存储JAVA方法调用和局部变量(方法内部调用的变量)局部变量的生命周期是和方法的生命周期是一模一样的当方法执行完成方法调用出栈局部变量也会销毁为什么JAVA虚拟机栈也是线程私有的呢?因为线程执行方法的局部变量只会有线程本身使用当前线程执行方法的局部变量其他线程是不会使用到这个变量使用到这个方法 JAVA虚拟机栈每一个线程在进行创建的时候会创建一个JAVA虚拟机栈它的内部保留的是一个一个的栈帧一个栈帧就对应着一个JAVA方法对应是一次一次的方法调用就对应着栈帧的入栈和出栈操作是线程私有的它本身对应的是一次一次的方法调用生命周期是和线程保持一致的它是主管JAVA程序的运行保存方法中的局部变量(8种基本数据类型和对象的引用地址)部分结果以及参与方法的调用和返回JAVA虚拟机栈随着线程的创建而创建随着线程的销毁而销毁 六)JAVA中的程序计数器:被执行引擎来进行解释执行 每执行完一行代码字节码执行引擎都会动态修改程序计数器里面的值 1)程序计数器是一块很小的内存空间几乎可以忽略不记它也是运行速度最快的内存区域 2)在JVM规范中每一个线程都有着它自己的程序计数器是线程私有的生命周期和线程的生命周期保持一致 3)任何时间一个线程只会有一个方法在执行也就是说所谓的当前方法程序计数器会存储对应的线程正在执行的JVM指令地址他是程序控制流的指示器分支循环异常处理线程恢复等基础功能都是需要这个程序计数器来完成字节码解释器工作的时候就是按照改变这个计数器的值来选取下一条需要执行的字节码指令 4)他也是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemory的区域 5)程序计数器就相当于是行号指示器相当于是迭代器和游标 1)操作局部变量表和操作数栈 2)将字节码指令翻译成CPU指令 1)使用PC寄存器存储字节码指令地址有什么用呢 为什么使用PC寄存器来记录当前线程执行的地址呢 CPU需要不停的进行切换各个线程这时候切换回来之后就得知道下一条从哪里开始继续执行JVM的字节码解释器就需要通过改变PC寄存器的值来确定下一条该执行啥样的字节码 CPU时间片就是CPU分配给各个程序的时间每一个线程被分配一个时间段称作是它的时间片在宏观上可以同时打开多个应用程序每一个程序并行同时运行但是在微观上由于只是有一个CPU一次只能处理程序要求的一部分如何处理公平一种方法就是引入时间片每一个线程交替执行 串行:用户线程和垃圾回收线程不能同时执行排队执行同一个时间点只能有一个线程执行垃圾回收线程只有一条执行逻辑 并行:线程可以并行同时地去执行 并发:一个CPU核心快速的切换几个线程让他们串行执行看着像是并行 并行:垃圾回收线程可以并行同时地去执行可以有多条但是执行用户线程的程序用户线程必须是一个停止的状态 并发:垃圾回收线程和用户线程是同时执行同时不一定是并行还有可能是交替执行不会是得用户线程出现Stop The World 七)JAVA中的栈: 栈的特点:栈是一种快速有效的分配内存方式访问速度仅仅次于程序计数器 JVM对于JAVA虚拟机栈的操作只有两个:每一个方法执行都伴随着进栈压栈和入栈 执行方法完成之后的出栈操作栈和程序计数器不存在垃圾回收问题虽然会溢出但是不需要GC资源会自动释放 1)每一个线程都有自己的栈栈中的数据都是依靠栈帧为基本单位格式进行存储的在这个线程上每一个执行的方法都是对应着一个栈帧栈帧是一个内存区块是一个数据集维持着方法执行的各种的存放的数据信息一个方法的执行对应着栈帧的入栈一个方法的结束对应着一个栈帧的出栈方法执行完成就出栈况且一个栈帧不可以调用另一个栈帧 这里面的抛出异常是未处理的异常会沿着栈向下抛出最后交给main函数 变量的分类: 按照数据类型划分分为基本数据类型和引用数据类型 按照在类中声明的位置来进行划分: 1)成员变量:在使用之前都默认经历过初始化赋值 静态成员变量也叫做类变量:在链接的准备阶段会给类变量赋初值并分配内存空间在初始化阶段给类变量进行赋值或者是静态代码块赋值 实例变量:随着对象的创建会在堆空间中分配实例变量空间并进行默认赋值; 2)局部变量:在使用之前必须要显示赋值否则编译不通过 JAVA虚拟机规范中允许JAVA栈的大小是动态的或者是固定不变的 1)如果采用固定大小的JAVA虚拟机栈那么每一个线程的JAVA虚拟机栈容量可以在线程创建的时候独立选定如果线程请求分配的栈容量超过虚拟机栈允许的最大容量JAVA虚拟机将会抛出一个StackOverFlower异常 2)如果JAVA虚拟机栈可以进行动态扩展并且在尝试扩展的过程中无法申请到足够的内存或者在进行创建新的线程的时候没有足够的内存去创建对应的虚拟机栈那么JAVA虚拟机将会抛出一个OutOfMemory异常是整体虚拟机内存都不够的情况下才执行 可以使用-Xss选项来进行设置现成的最大栈空间栈的大小直接决定了函数调用的最大可达深度是以字节为单位-Xss256K  1)栈溢出的情况:StackOverFlow当加栈帧的时候栈空间不足会发生StackOverFlow 2)调整栈大小就能保证栈不溢出吗 能当进行有限递归的情况下可以解决 不能对于死循环来说循环递归只能增加递归的深度但是最终还是会溢出只能降低递归最后出现的时间增加栈的大小对于有限的递归来说可能会避免堆溢出 3)垃圾回收不会涉及到虚拟机栈程序计数器没有GC也没有error虚拟机栈没有GC出栈就是垃圾回收但是存在error本地方法栈不存在GC存在error方法区会存在GC和error方法区放时间比较长的数据有error和GC 4)分配的栈越多越好吗 避免出现StackOverFlow的概率会降低整个JVM分配内存空间有限的可能会挤占其他的空间 堆和栈有什么区别 1)存放的数据内容不同 2)大小不同 3)访问数据性能不同:因为堆很大进行查询对象的时候需要进行寻址和内存管理但是栈存放基本数据类型内存固定不需要有动态数据的变化不需要进行访问再多大部分情况下从栈上面寻找数据是很快的不需要寻址和内存管理 4)功能侧重点不同:堆是JAVA虚拟机的主要存储单位JAVA中的对象和数组都是保存在这个区域的而栈式JAVA虚拟机基本的运行单位也就是说堆主要解决的是数据的存储数据怎么放放在哪里的问题但是栈主要解决的是JVM程序方法运行的调用关系以及运算在哪里进行运算在那里进行存储数据如何处理数据的 栈是运行时的基本单位而堆是存储的单位栈解决的是程序的运行问题就是程序如何执行如何处理数据把相关的一些指令通过栈的局部变量表和操作数栈来进行体现但是主体的数据都放在堆中左上是局部变量左下是字节码指令右边是堆空间 符号引用:符号引用是一种字面上的引用它使用符号来描述所引用的对象比如说类名方法名和字段名符号引用在编译时期就已经存在了它是一种无法直接定位到内存地址的引用直接进行编译还没有进行类加载就是一个占位符标记着内存的一个位置 直接引用定义:直接引用就是直接指向对象内存地址的引用它包括创建对象的new操作符获取对象的引用或者是实例变量的操作直接引用在运行时才存在他是可以直接定位到内存地址的引用根据内存地址可以直接拿到对象的引用直接可以定位到字面量的引用地址 字面量:实际的字符串判断字符串常量池是否存在该字符串的依据如果key存在那么直接就把value值赋值给实际的引用如果没有就现存 从HotSpot虚拟机来说字符串常量池是依靠C的HashMap来实现的key是字符串常量的字面量value是字符串对象的引用 编译期常量池:在编译期间可以确定的常量 运行期常量池:在运行期间可以确定的常量String的intern()方法将动态生成的东西就是放在运行期常量池的 一)栈帧: 一个栈桢的入栈对应着方法的调用一个栈桢的出栈对印着方法的执行的结束如果发生异常还会将异常抛给方法调用者栈帧的大小决定了栈中能存放栈帧的个数每一个栈帧中存放着局部变量表操作数栈(或者是表达式栈)动态链接是指向运行时常量池的方法引用方法返回地址是方法正常退出或者异常退出的定义还有一些附加信息 二)局部变量表:局部变量表又被称之为是局部变量数组或者是本地变量表 对于int double float本身就是数值char有对应的ASCILL值可以看作数值存储可以转化成intbool类型8中基本数据类型和引用类型都可以使用数值类型来表示不会涉及到线程安全问题局部变量表大小一旦确定下来是不会更改的本质上来说就是一个数字数组用于存储方法参数和本地的局部变量这些数据类型包括各种基本数据类型对象引用以及各种返回值类型由于局部变量表是建立在线程的栈上面是线程的私有数据因此不存在线程安全问题还有就是局部变量表的大小是在编译时期确定下来的在方法运行期间是不会修改局部变量表的大小的 方法返回值类型访问标识是public static上面包含着方法声明的所有信息 下面字节码指令行号和源代码行号的对应关系 按照变量声明的位置依次占据着索引位置根据索引位置来使用变量Descibler:表示变量类型length:描述当前变量作用域的范围 起始PC:字节码执行的行号也是表示变量作用域的起始位置也就是变量声明完以后 起始PClength代码的长度CodeLength 变量槽:this变量存在与普通方法和构造方法的局部变量表但是静态方法没有this序号就是局部变量表的位置引用数据类型是一个槽位 index:代表变量所占槽位的起始位置 三)操作数栈:底层是使用数组结构来实现的 3.1)方法执行过程中就是在执行字节码的指令操作数栈都是临时存储数据弹出栈栈中的数据也就没了但是局部变量表中的数据永远都是存在的 3.2)操作数栈是存放临时数据的地方两个数相加运算都要放在操作数栈最终结果都放在操作数栈中也就是在编译期间确定了大小只能由入栈出栈操作不能通过索引调用 一些字节码指令向操作数栈中存放数据也可以从操作数栈中取出数据 3.3)局部变量表是存放方法中局部变量的位置在编译期间就确定了数组的长度是在方法中声明的局部变量局部变量表方法形参方法内部定义的变量底层是依靠数组来实现的实际上是依靠定义变量的顺序来声明数组下标的 每一个独立的栈桢中除了包含局部变量表以外还包含着一个先进先出的操作数栈也可以称之为是表达式栈操作数栈在方法执行过程中向栈中写入数据或者是提取数据就是入栈或者是出栈比如说操作数栈在执行某一些字节码指令的时候向栈中写入数据也就是将值压入操作数栈中其余的字节码指令再将将操作数取出操作数栈中执行复制交换求和等操作的时候再将它们写回到操作数栈中   byte short int boolean都以int型来保存 执行引擎要把字节码指令翻译成机器指令再来进行操作操作数栈   istore_i:将操作数栈中的数据取出来放到局部变量表中的对应位置i那么到底应该放在哪一个位置呢应该在istrore后面加上一个数组下标比如说istore_1就会将操作数栈中的内容放到局部变量表中的1号位置局部变量表中的数据取出来之后就没了 iload_i:从局部变量表的i位置复制一份取出数放到操作数栈中最终操作数栈和局部变量表都是会有这个数据的 iconst_data:将data数据放入操作数栈中 int i0会拆解出iconst_0和istore_1这两个指令 i_add:将操作数栈中的顶部的两个数据进行相加并将结果放入到操作数栈中 iinc 1 by 1:将局部变量表中的1号位置加1 操作数栈的指令非常多缓存一般缓存在物理寄存器中从而来提升CPU的读写效率执行速度快就比如说add操作 三)动态链接:指向常量池的方法引用 重点:每一个栈帧内部包含着一个指向运行时常量池的该栈帧所述方法的引用包含这个引用的目的就是为了支持当前的方法可以实现动态链接因为JAVA源文件被编译成字节码文件的时候所有的变量和方法引用都作为符号引用保存在class文件的常量池里面比如说一个方法调用了另外的其他方法的时候就是通过常量池中指向的方法的符号引用来表示的所以动态链接的作用就是为了将符号引用替换成直接引用 大部分字节码指令执行的时候都是要对常量池中的访问在桢数据区中就保存着能够进行访问的常量池的一个指针方便访问常量池 在编译时时常量池里面包含着类似于键值对的信息key是符号引用就是带有#的value是真实的字面量或者是接口信息等真实结构返回值类型void数据类型int父类信息System结构的加载比如说很多方法都是没有返回值类型的那么这些函数就是都可以引用void的符号引用方法名字value后面可能还是包含着符号引用类加载过程中的使用到的信息都作为一个符号声明出来   运行时常量池:就是为了提供一些符号和常量便于指令的识别 五)方法返回地址:记录PC寄存器存储的值作为返回地址 PC寄存器的地址值方法调用者调用该方法的下一条指令地址 八)JAVA中的堆 1)一个java进程对应这个一个JVM实例堆是JAVA内存管理的核心区域Runtime就对应着一个运行时数据区一个进程中的多个线程共享同一份堆空间和方法区而栈和程序计数器使每一个线程私有的JAVA堆区在JVM启动的时候就被创建了它的空间大小也被确定了是JVM管理的最大的一块内存区域JAVA虚拟机规范中规定对可以处于不连续的物理内存空间中但是在逻辑上应该是连续的所有的线程共享JAVA堆空间在这里还可以划分出线程私有的缓冲区 2)通过-Xms10m -Xmx10m是初始堆空间和最大堆空间 3)堆空间在物理上可以不连续逻辑上是连续的而栈只存在入栈和出栈不会有垃圾回收 4)堆上面有着各个线程的缓冲区每一个线程都有着自己的小空间这个小空间就是TLB这个时候并发性会更好 5)不是没有引用指向对象对象就立即回收只有说堆中快满了不足了才会执行GC垃圾回收不是栈中的引用弹出栈对象就立即被回收了否则GC的频率过高就会影响用户线程的执行如果一直进行垃圾回收就会影响用户线程的执行所以应该将堆空间的区域设置的大一些合理的分配减少GC让用户线程执行时间长一些提升吞吐量频繁的GC会影响性能都是Throwable的子类error不可控exception一般需要手动去捕获比如说javaheapspace 6)-Xms10m -Xmx10m -XX:PrintGCDetails(打印GC的详细信息)注意-Xms是用来设置堆空间年轻代老年代的最大内存大小 7)JAVA虚拟机规范对于堆的描述是:所有的对象实例和数据都应该当运行时分配在堆上当方法结束以后堆中的对象不会马上移除仅仅是在垃圾收集的时候才会被移除 8)存储在JVM的JAVA对象可以被分成两类一类是生命周期比较短的对象这类对象的创建和消亡都十分的迅速但是另一类的对象的生命周期非常长在某一些极端的情况下还可以和JVM的生命周期保持一致JAVA堆进一步划分可以划分成年轻代和老年代年轻代又可以分成Eden区Survivor1区和Survivor0区空间有时候也叫做from区和to区 查看堆占用的情况:    伊甸园区:幸存者1区:幸存者2区8:1:1 默认新生代和老年代的占比是21 新生代:伊甸园区幸存者1区幸存者2区 yonggc/minor GC:触发条件时伊甸园区满了幸存者区满了不会触发yonggc但是yongGC回收的过程中会顺便带上回收幸存者区伊甸园区满了之后再来对象这个时候再次进行垃圾回收会产生STW需要进行判断各个对象是否是垃圾不是垃圾的移动到幸存者1区或者是幸存者2区放在空的区空的幸存者区是to(从伊甸园区的对象首先放到幸存者1区)不空的是from 什么时候执行垃圾回收:任何时候都可能当系统觉得你内存不足了就会开始回收常见的比如分配对象内存不足时这里的内存不足有可能不是占用真的很高可能是内存足够但是没有连续内存空间去放这个对象当前堆内存占用超过阈值时手动调用 System.gc() 建议开始GC时系统整体内存不足时等 1)如果幸存者0区或者是幸存者1区放不下伊甸区的对象直接将对象放到老年代 如果说伊甸园区和幸存区的比例比较大的话也就是说幸存者1区和幸存者2区所占的空间比较小理想情况下所有的伊甸区中的垃圾对象都被回收了很少的对象存活到幸存区但是一般情况下幸存区的对象比较少如果再向伊甸园区放对象很容易导致伊甸园区对象放在幸存区幸村区容易存不下于是直接给放到old区会导致minorGC失去意义因为正常进行yongGC的时候幸存者1区和幸存者2区也会进行垃圾回收而现在有些对象没有达到阈值15就直接到达老年代了minorGC意义不大况且尽量说先把对象在新生代回收分代意义更小 2)如果伊甸园区比较小那么yonggc会频繁触发会影响用户进程影响STW的时间 MarkWord里面存放对象的GC年龄只有4位所以最大也只是15 3)复制之后有交换谁空谁是to 图解对象分配的过程 - 掘金 (juejin.cn) 1)当创建一个新的对象的时候首先进行判断伊甸区是否可以存放的下如果放得下就为其分配内存如果放不下的话那么就开始yongGc 2)然后再进行判断伊甸区是否可以存放的下如果放得下的话就为其分配内存如果放不下的话说明这个对象比伊甸园区的对象还要大此时说明这个对象是一个超级大的对象此时就直接放在老年代如果老年代也是放不下就直接进行FullGC然后进行判断老年代是否可以存放的下如果放得下就直接进行存放如果还是村放不下就是直接报OOM异常 3)有的时候进行YGC的时候幸存的对象会进入到幸存区此时会进行判断是否存放的下如果存的下就存放存不下就直接放入到老年代 4)JVM调优:GC回收能够少一些GC扫描的过程中会出现STW用户线程终止 从频率上看大部分情况下都是回收的是新生代只有伊甸园区满的时候才会触发youngGC只有触发yongGC才会顺便回收幸存者1区或者是幸存者2区 5)年轻代GC触发机制:当年轻代空间不足的时候就会触发minorGC TLAB:默认给每一个线程开辟一块内存空间存放线程自己的对象 Class对象是存放在堆区的不是方法区类的元数据元数据并不是类的Class对象Class对象是加载的最终产品类的方法代码变量名方法名访问权限返回值等等都是在方法区的代码信息只是在方法区 对齐填充:方便计算机寻址存取方便是计算机寻址最优的一种方式 1)大对象直接放到老年代:可以设置参数 1.1)如果你知道系统创建的对象比较大况且这些对象不会被垃圾收集的就可以配置此参数提早进入老年代 JVM会直接判断对象大小就是为了降低大对象分配内存的时候复制对象而降低效率 1.2)大对象就是需要大量连续内存空间的对象比如说数组和字符串JVM参数-XX:PretenureThreshold可以设置大对象的大小如果对象设置超过大小会直接进入到老年代不会进入到年轻代这个参数只是在Serial和ParNew两个收集器下面有效 1.3)比如说设置JVM参数: -XX:PretenureThreshold(单位是字节) -XX:UseSerialGC设置这个参数以后会发现大对象直接进入到老年代况且这个大对象占用空间还比较大可能触发频繁的YGC早早的被回收腾出更多的空间给朝生夕死还有为了大对象是分配内存时候的复制操作而降低效率 2)达到分代年龄存放到老年代:可以设置参数默认值是15 什么时候这个参数设置的比较小一点呢 大概知道很多new对象生命周期不是特别长这些对象可能做几次GC就会被垃圾回收掉对象new出来以后可能要用一段时间程序员大概可以估算到一个方法中有一个大对象但是方法结束非常快对象可能一两次GC就被干掉分代年龄尽量设置的短一些这个参数设置的比较小说明系统中的对象大多数经历一次或者是两次GC就会被干掉要么经历很多次GC都不会被干掉就可以把分代年龄设置比较小已经知道大多数对象明明生命周期很长这时候分代年龄设置的短一些就不要白白的在新生代复制来复制去浪费性能趁早滚去老年代早早的在伊甸区腾出空间 3)对象动态年龄判断机制:  当前放对象的Survior区域里面(其中一块区域放对象的那一块S区)一批对象的总大小大于这块Survior区域的50%(-XX:TargetSurivorRatio可以指定)那么此时大于等于这一批年龄对象的年龄最大值的对象就可以直接存放到老年代了假设现在幸存者区里面有一批对象年龄1年龄2年龄N的多个年龄对象超过了Surivor区域的50%此时就会把年龄N以上的对象全部放在老年代这个规则是希望那些可能长期存活的对象尽早地进入到老年代对象动态年龄判断机制其实是在minor GC以后触发的 为什么1s之后就会变成垃圾对象呢因为生成订单对象只是一个方法方法很快就会结束的GCroots很快被销毁这个Order引用所指向的对象也会很快地变成垃圾如果是按照上面的参数进行设置的话可能会频繁的触发FullGCFullGC是需要优先解决的对于订单系统来说每秒钟有60M对象会向伊甸园区里面存放1S以后变成垃圾对象 1)因为每一秒钟大概有60M的对象要往伊甸园区进行存放因为伊甸园区大概是800M大概13秒或者14s来说伊甸园区就会被放满第14s的对象尝试存放到伊甸园区会触发minorGC会把伊甸区的对象全部进行垃圾回收前面13s的对象做minor GC的时候都是可以回收掉的但是伊甸区第14s产生的对象这一时刻尝试放到伊甸园区会触发minorGC因为此时订单正在执行过程中第14S会发生STW第14s产生的对象都被GCROOTS引用着所以此时这60M对象会被存放到S0幸存者区域但是前面13s产生的对象(因为方法已经结束了况且已经知道1s以后对象会被回收)会被伊甸园区GC直接被干掉因为之前伊甸区的方法已经结束了生成订单非常快 2)按照上一步的分析第14s产生的对象会被直接存放到幸存者区域因为幸存者区域此时还可以存放的下 3)但是最终情况是由于动态年龄判断机制这60M对象会被分配到老年代(即使幸存者区域可以存放100M的对象)每隔14s就有60M对象放到老年代(前13s产生的对象在新生代第14s产生的对象直接放到老年代)等到一段时间5 6min老年代放满就会发生一次FullGC但是其实这些老年代的对象其实早就变成垃圾了因为正常的订单对象早就变成垃圾了因为垃圾对象频繁的进行FullGC肯定不太好现在根据空间分配担保机制来看第14s的60M对象 已经超过了这块Survior区域的50%此时这60M对象很快就会进入到老年代 4)这个时候朝生夕死的对象太多于是就适当提高年轻代的空间大小因为在这种情况下系统会产生大量的朝生夕死的对象频繁导致FullGC的原因就是动态年龄判断机制使用两种机制来优化一种是调整surivor区域一种是把整个年轻代调整的大一些几乎不发生FullGC 适当提升新生代的比例之后第24s以后对象的空间已经满了那么此时这个25S产生的60M对象会直接存放到幸存区此时触发minorGC会进行回收伊甸园区和幸存者区不光13s产生的对象被回收了第14s产生的60M对象也被回收了 4)老年代动态分配担保机制:本质上是保证年轻代的非垃圾对象做了FullGC到了老年代以后避免老年代空间存放满而触发FullGC 年轻代每一次做minorGC之前JVM都会做校验计算一下老年代的剩余可用空间大小 1)首先会进行校验如果老年代的可用空间大小小于年轻代现有的所有对象之和包括垃圾对象就是看看老年代剩余可用的空间是否容纳年轻代的所有对象如果可以容纳的下直接做minorGC如果容纳不下也就是说老年代所剩空间不多了就会看一个参数如果这个参数没有设置就会触发FullGC将老年代和年轻代一起进行回收如果回收完成还没有空间存放新对象就会发生OOM 2)如果这个参数设置了就会看看老年代的可用内存大小是否大于之前所有每一次minor gc后进入老年代的对象的平均大小如果小于就会触发FullGC将老年代和年轻代一起进行回收如果回收完成还没有空间存放新对象就会发生OOM节省一次MinorGC 3)当然如果minorGC以后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间那么也是会触发FullGC然后再来一次monor GC如果FullGC之后还是没有足够的空间来存放minor GC以后的存活对象此时就会发生OOM 实质:就是在minorGC之前判断是否大概率发生FullGC如果大概率发生FullGC 如果调整完新生代的大小之后将晋升到老年代的空间调整成5了因为这种情况是每25S触发一次minorGC触发一次GC分代年龄1分代年龄达到5说明已经过去了好几分钟了所以说这些已经达到5的对象早就已经变成垃圾了要么赶紧被清理掉如果分代年龄达到5说明这样的对象肯定不是简单的GC对象肯定不容易被收集这样的对象肯定是系统中的缓存对象Spring Bean对象线程池的引用对象对于这些对象可以让她尽量的早点老年代不要再年轻代里面占用过多的那些朝生夕死的对象的空间了订单对象库存对象优惠劵对象的空间了让那些年轻代的对象能够在年轻代就被干掉   什么时候发生FullGC? 1)system.gc:此方法的调用是建议JVM进行Full GC虽然只是建议但是不一定但是很多情况下他会触发Full GC从而增加FullGC的频率既增加了间歇性停顿的次数强烈建议能不使用该方法就不使用该方法让虚拟机自己去管理他的内存可以通过-XX:DisableExplicitGC来禁止JVM调用System.gc() 2)老年代空间不足:老年代空间只有在新生代空间转入以及创建大对象大数组的时候才会出现空间不足的状况当执行Full GC之后如果空间还是不足那么抛出以下错误: java.lang.OutOfMemoryjava heap space为了避免以上两个状况引起的Full GC调优的时候尽量做到让对象在minor GC阶段被回收让对象在新生代多存货一段时间以及不要创建大对象或者是大数组 进行minorGC的时候要将新生代的对象往老年代里面进行存放先看看老年代的最大可用连续空间是否大于新生代所有对象的总空间如果比他大那么说明这里面最坏的情况就是新生代的对象没有一个是垃圾老年代肯定都是可以放得下如果小于说明此时minorGC不安全那么继续判断这个参数如果这个参数是true那么会进行继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小老年代GC垃圾回收慢STW时间太长 为什么要进行分代呢 其实部分带也完全可以因为分代的唯一理由就是来优化性能如果没有分代那么所有的对象都在一起就好像把一个学校的所有人都关在教室里面GC要找到那些对象没有使用这样子就会对堆的所有区域进行扫描但是很多对象都是朝生夕死的如果进行分代的话吧新创建的对象放在一个地方当GC的时候就把这块存储朝生夕死的对象的区域进行回收这样子就会腾出很大的空间来 因为使用G1垃圾回收器内部算法耗费的性能比CMS要高CMS触发FullGC比例可能会导致部分空间不可用如果FullGC发生频率很低这时就可以启动serial Old来进行清理设置参数是0每一次FullGCserial Old会清理一次内存碎片但是如果出现秒杀活动就尽量减少内存碎片的整理因为不敢让用户线程停止这时候就可以配置5次FullGC一次清理如果长时间不做内存清理那么老年代的连续可用内存空间会越来越少 逃逸分析: 1)因为JVM内存分配都是在堆上面进行分配当对象没有引用的时候需要依靠GC来进行回收如果对象数量比较多的时候就会给GC带来巨大压力也间接影响了GC的性能为了减少临时对象在堆中分配的数量JVM本身就会通过逃逸分析确定对象不会被外部访问如果不会逃逸那么可以将对象在栈上分配内存这样子对象所占用的内存空间就可以随着栈帧出栈而销毁从而减轻了垃圾回收的压力 2)对象的逃逸分析:对于一个对象来说如果采取栈上分配不会new一个对象在栈上面而是将它的成员变量属性剥离开存放分配在栈上分开存这几个字段会进行标识是属于哪一个对象的前提是开启了逃逸分析减少GC的压力线程结束方法结束对象就被销毁对象的逃逸分析就是动态的进行分析对象的作用域当一个对象在方法中定义的时候他可能被外部方法所引用比如说调用参数传递到其他方法里面 很显然Test1中的user对象被返回了这个对象的作用域的范围是不确定的Test2中的方法中的user对象可以确定方法结束以后对象在栈上面就被销毁了就是一个无用的对象这样的对象其实我们就可以把他分配到栈空间上面让方法结束以后随着栈内存一起释放掉 JVM对于这种情况可以可以开启逃逸分析参数(XX:DoEscapeAnalysis)来优化对象内存分配位置通过标量替换优先分配在栈上JDK7之后默认开启逃逸分析如果要关闭使用参数(-XX:-DoEscapeAnalysis) 1)标量替换:通过逃逸分析以后发现确定该对象不会被外部访问并且对象可以进一步被分解的情况下JVM不会创建该对象而是将对象的成员变量拆解成若干个被这个方法所使用的成员变量所代替这些所代替的成员变量在站镇上或者是寄存器上面分配空间这样就会保证不会有一大快连续的内存空间导致对象内存不够分配开启标量替换参数(XX:EliminateAllocations)JDK7之后默认开启这个时候不是在栈上开辟一个对象而是将对象的成员变量拆分开存放拆分成成员属性分配到栈上面 2)标量和聚合量:标量是不可以进一步被分解的量JAVA中的基本数据类型就是标量比如说int double 引用数据类型而聚合量就是可以进一步被分解的量这种量称之为是聚合量在JAVA中对象就是一种可以被分解的聚合量栈上分配依赖于逃逸分析和标量替换 没有发生逃逸的对象就可以分配在栈上随着方法的结束栈空间就会被移除 1)栈空间是每一个线程一份不会涉及到同步的问题可以并行地去执行 2)每一个栈都是放的是栈帧当方法执行完栈帧弹出栈空间被释放内存自动释放不会涉及到GC //1.不会发生逃逸分析 public static String createStringBuffer(String s1,String s2){StringBuffer snew StringBuffer();s.append(abc);s.append(bcd);return s.toString();} //2.会发生逃逸分析 public static StringBuffer createStringBuffer(String s1,String s2){StringBuffer snew StringBuffer();s.append(abc);s.append(bcd);return s;} 如何快速判断发生了逃逸分析:只是看new的对象实体是否在方法外部使用 使用逃逸分析编译器可以对以下代码进行优化: 1)栈上分配:将堆分配转化成栈上分配如果一个对象在子程序中被分配要使得指向该对象的指针永远不会发生逃逸对象可能是栈分配的首选而不是堆分配栈上分配减少垃圾回收  2)同步省略:如果一个对象被发现只能被一个线程被访问那么对于这个对象的操作可以不考虑同步多个线程来起不到同步的效果纯看字节码文件还是有锁的但是程序会自动优化掉 因为线程同步的代价是相当高的同步的后果就是降低并发性和性能再进行动态编译同步代码块的时候JIT即时编译器还会借助逃逸分析来判断同步代码块使用到的锁对象是否只能被一个线程访问而没有被发布到其他线程如果没有那么JIT即时编译器就会在编译这个同步代码块的时候会取消对于这段代码的同步这样就可以大大提高并发访问的性能这个取消同步的过程就叫做同步省略也叫做锁销除 代码中针对object这个对象进行加锁但是object对象生命周期只会在方法中使用到并不会被其他线程访问到所以在JIT即时编译过程中就会被优化掉优化成: 3)分离对象或者是标量替换:有的对象可能不需要作为一个连续的内存就可以被访问到那么对象的部分或者是全部可以不存储在内存而是存储在CPU寄存器中  把一个对象替换成两个局部变量放到局部变量表里面了将对象打散了分配到栈上 java中的引用类型一般分为四种: 1)强引用:public static User usernew User()在JAVA程序中最常见的引用类型就是强引用也就是最常见的普通对象引用也是默认的引用类型在JAVA语言中使用一个new关键字操作符创建一个新的对象的时候并将其赋值给一个变量的时候这个引用就是指向该对象的一个强引用强引用的对象是可达的垃圾回收器永远也不会回收此类对象对于一个普通对象来说只要超过了引用的作用域或者显示的将强引用的引用赋值为null也就是可以当作垃圾来收集了当然具体回收策略还是依靠垃圾收集策略 2)软引用:将对象使用SoftReference软引用类型对象包裹起来的正常情况下不会进行垃圾回收但是GC做完以后发现释放不出新的空间来进行存放新的对象这时GC就可以将这些软引用的对象直接进行回收掉软引用可以用来实现内存敏感的高速缓存 pulic static SoftReferenceUser referenece new SoftRefernceUser(user); 软引用在实际中有一个重要的应用就是比如说浏览器的后退按钮按下后退的时候这个后退的时候所显示的网页是重新进行请求还是从缓存中取出呢这要看具体的实现策略了 如果一个网页在浏览结束以后就进行内存的回收那么后退查看前面所浏览的网页的时候需要重新进行构建如果浏览到的网页结构存储到内存中会造成内存的大量浪费可能会造成空间溢出 3)弱引用:将对象使用WeakReference软引用的类型进行包裹弱引用和没引用差不多只要出现垃圾回收就会被回收掉 4)虚引用又被称之为是幽灵引用或者是幻影引用几乎不用 5)finalize方法来判断对象是否存活 1)即使在可达性分析算法中不可达的对象也并非是非死不可的这个时候他们正在处于缓刑阶段要想宣告一个对象真正的死亡至少要经历两次标记过程标记的前提就是对象在进行可达性分析算法以后发现没有和Gc Roots相连接的引用链 2)第一次标记并进行一次筛选:筛选的条件就是此对象是否有必要执行finalize()方法如果当前该对象没有重写finalize方法那么对象直接被回收 3)第二次标记如果这个对象重写了finalize方法finalize方法是对象脱逃死亡命运的最后一次机会如果对象要在finalize中成功拯救自己只要重新和引用恋上面的任何的一个对象建立关联即可假设说把自己赋值给某一个类变量或者是某一个对象的成员变量那么在第二次标记是将他移除掉即将被回收的集合如果这时候对象还没有被回收那么此时就真的被回收了注意一个对象的finalize方法只会被执行一次也就是说调用finalize方阿飞自我救命的方式只有一次 public class Demo {static ListUser listnull;static class User{public String username;public String password;Overrideprotected void finalize() throws Throwable {System.out.println(对象即将被回收);}}public static void main(String[] args) {listnew ArrayList();while(true){list.add(new User());new User();}} fullGC回收类元信息和堆空间没有类原信息对象的对象头已经没有指针指向这个类原信息 三种类加载器不会被回收所以加载的类不会被回收类加载器会维护他所有加载过的类所有的classLoader都会记录着所有它加载过的类信息但是JSP的类加载器和自定义的类加载器是可以回收第三条Class对象也应该被回收因为Class对象可以创建出新的对象  6)为什么要有TLAB  1)堆区是线程共享的区域任何线程都是可以访问到堆区的共享数据由于对象的实例的创建在JVM中非常频繁那么在并发环境下从堆区中分配内存空间是线程不安全的TLAB的作用就是为了避免多个线程操作同一块地址空间需要使用到加锁等机制进而影响分配速度 2)从内存模型而不是垃圾收集的角度他会针对于Eden区会继续进行划分JVM为每一个线程都分配了一个私有的缓存区域它被包含在伊甸园区里面 3)多线程同时分配内存的时候使用TLAB是可以避免一系列的非线程安全问题同时还可以提升内存分配的吞吐量因此我们可以见这种内存分配策略称之为是快速内存分配策略 1)尽管不是所有的对象实例都能够在TLAB中能够重新分配内存但是JVM确实是将TLAB作为内存分配的首选 2)在程序中开发人员可以通过选项-XX:UseTLAB设置是否开启TLAB空间 3)默认情况下TLAB空间的内存非常小仅占用TLAB空间所占用的Eden区空间的百分比大小的1%当然也是可以通过-XX:TLABWateTargetPercent来设置TLAB空间所占用的Eden空间的百分比大小 4)一旦对象在TLAB空间分配内存失败的时候JVM就会尝试通过加锁机制来确保数据操作的也从而直接在伊甸园区空间分配内存
http://www.w-s-a.com/news/516881/

相关文章:

  • 商洛网站建设哪家好网站建设 织梦者
  • 怎么创建收费网站宁夏住房和城乡建设部网站
  • 怎么确认网站是什么语言做的用php和mysql做网站
  • 安徽做网站的公司有哪些星子网络公司
  • 肥西县重点工程建设管理局网站wordpress界面菜单怎么弄
  • 宁夏网站开发设计说明书wordpress主题背景图片
  • 同一个阿里云可以做两个网站吗织梦 帝国 学校网站
  • 城阳网站建设培训网站后台怎么上传文件
  • 重庆茂尔建设集团有限公司网站网页制作教程软件
  • 金湖建设工程质量监督网站高端网站建设公司哪里济南兴田德润实惠吗
  • 站酷设计网站官网入口文字设计seo网站推广工具
  • 专业移动网站建设网站建设软件dw
  • 摄影网站设计思想视觉传达毕业设计作品网站
  • 需要优化的网站有哪些设计装修app
  • 数据型网站建设东莞好的网站国外站建设价格
  • 网络营销方法有哪些举例seo应用领域有哪些
  • 建设银行官方网站官网做网站的专业叫什么
  • 矿区网站建设濮阳做网站的公司有哪些
  • 有什么网站可以自己做书甘肃建设厅网站首页
  • 门户网站建设哪专业怎么把自己做的网站登录到网上
  • 如何做网站小编餐饮业手机php网站
  • 备案 网站商城网站设计公司排名
  • 汕头做网站优化公司seo软件简单易排名稳定
  • 如何做众筹网站微网站设计平台
  • 富平做网站十堰优化seo
  • 免费网站空间可访问wordpress弹窗注册代码
  • 东莞网站建设教程南京做代账会计在哪个网站上找
  • 网站开发好了 怎么发布wordpress数据库缓存插件
  • 工业电商网站怎么配色社交网站建设平台
  • 使用pycharm网站开发建一个网站需要什么条件