JVM基础知识和调优

什么是垃圾

当一个对象有人引用它时,那么就不是垃圾,不然就不是垃圾

如何辨别一个对象是不是垃圾

计数(最基础的),有一个对象引用就记一个数(i++)问题,循环引用

GC roots

定位不是垃圾的对象,剩余的都清理掉,从引用根节点进行跟踪,内存中没有跟踪到的就是垃圾

垃圾回收算法(三大类)

标记删除(mark-sweep)、拷贝(copying)、标记压缩(mark-compact)

标记删除:就是把垃圾区域标记出来,然后清除出去(问题:碎片化)

拷贝:先将内存分成俩半,把要清除的一半有用的区域复制到另一半,然后清除这一半(浪费空间)

标记压缩:对内存进行整理,有用的堆到前面,没用的堆到后面,一边标记一边整理(效率低)

算法运用

分代模型:内存分为俩大块,一个是新生代区域,一个是老年代区域

新生代:就是刚刚new出来的对象(用拷贝(copy)回收,效率高,90%以上)

老年代:经过了好多次垃圾回收,回收不了的对象(不容易回收的对象)(用标记删除或标记压缩回收)

每当垃圾回收器去新生代回收对象时,有些对象没有被回收走,那么这个对象就会“涨一岁”(标记+1)又来回收,又没被收走,那么就会又“涨一岁”,直到到达一点的年龄段后(一般默认是15 CMS是6),被归入老年代

新生代回收:因为回收效率高(90%以上),代表大部分都会被清理出来,就不需要把新生代内存空间分成很大的块,而是分成三个区域:eden(新生对象,刚刚创建的)、surviver1(幸存区1)、surviver2(幸存区2)

  • 当GC去eden区回收对象时,会有小部分对象不是垃圾,就会拷贝(copy)到surviver1区域,然后整个eden清除
  • 当第二次GC的时候,surviver1中的对象依旧不是垃圾,就会拷贝(copy)到surviver2区域,也会包括eden区域中新生对象、然后清空eden和surviver1区域
  • 第三次依旧,就是surviver2+eden->surviver1,反正就是这俩surviver里面反复复制,然后清空另一半和eden

总结

当new一个对象出来的时候,先会在栈上分配,因为线程里面是方法调用方法的,当一个方法执行完出栈,那么对象也跟着出栈,所以根本就不需要GC!!

细节:逃逸分析、标量替换

逃逸分析:不能有别的方法引用这个方法里面的对象(不然出现野指针)

标量替换:就是这个对象可以被一些基础类型的对象替代(如:对象里面是两个int,就可以用两个int去替代它)

对象在JVM中的出生到死亡

TLAB:Thread Local Allocation Buffer(线程本地分配缓冲区)eden区有一个线程本地缓冲空间,避免争抢,如果线程需要,就new到线程自己身上去

FGC:全体回收,年轻代老年代全体回收

垃圾回收器(十种)

2~7分代模型(内存分成很大的块,新生代老年代、成对出现) 8~10分区模型(内存分成很小的一个块)

  1. Epslion:最没用的,就是个摆设,就调试用的到(Debug)
  2. Parallel Scavenge:采用多线程切GC
  3. Parallel Old:采用多线程切GC
  4. Serial:stw(stop the world)单线程,停止工作线程,进行GC(很少用了)
  5. Serial Old:单线程,停止工作线程,进行GC(很少用了)
  6. CMS(重点!!!!):工作在老年代
  7. ParNew:和Parallel Scavenge一样,只不过是用来配合CMS
  8. G1
  9. ZGC
  10. Shenandosh
  11. GO(没有垃圾回收器)
  12. Rust(没有GC!)

CMS:四个阶段

  • 初始标记:标记老年代root对象
  • 并发标记:对root对象往下进行标记
  • 重新标记:修正错标的内容(三色标记算法)
  • 并发清理:清理垃圾,也会产生浮动垃圾,下次处理

产生混乱,

会产生浮动垃圾,但并不要紧,可以下次来回收

最严重的的是错标

解决方案:三色标记

问题解决:有俩种

CMS的:把A变成灰,但是会出现一个巨大的问题!

  • 就是这是在多线程并发的情况,就是垃圾回收线程1标记完属性1,工作线程把属性1指向白色对象D,
  • 垃圾回收线程2因为发现又有指向了,就把A变为灰色
  • 而垃圾回收线程1标记完属性2,认为全部标记完了把A又变成了黑色,这样对象D就找不到了,也就无法回收!!


最后可以使用arthras调优java程序 阿里巴巴开发

JVM内存和对象

基本问题

  • 介绍下 Java 内存区域(运行时数据区)
  • Java 对象的创建过程(五步,建议能默写出来并且要知道每一步虚拟机做了什么)
  • 对象的访问定位的两种方式(句柄和直接指针两种方式)

拓展问题

  • String 类和常量池
  • 8 种基本类型的包装类和常量池

一 概述

对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像 C/C++程序开发程序员这样为每一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题。正是因为 Java 程序员把内存控制权利交给 Java 虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会是一个非常艰巨的任务。

二 运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。JDK. 1.8 和之前的版本略有不同,下面会介绍到。

JDK 1.8 之前:

JDK 1.8 :

线程私有的:

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

线程共享的:

  • 方法区
  • 直接内存 (非运行时数据区的一部分)

2.1 程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

另外,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

从上面的介绍中我们知道程序计数器主要有两个作用:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

2.2 Java 虚拟机栈

与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。

Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。 (实际上,Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。)

局部变量表主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

Java 虚拟机栈会出现两种错误:StackOverFlowError 和 OutOfMemoryError。

  • StackOverFlowError: 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
  • OutOfMemoryError: 若 Java 虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出 OutOfMemoryError 错误。

Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

扩展:那么方法/函数如何调用?

Java 栈可用类比数据结构中栈,Java 栈中保存的主要内容是栈帧,每一次函数调用都会有一个对应的栈帧被压入 Java 栈,每一个函数调用结束后,都会有一个栈帧被弹出。

Java 方法有两种返回方式:

  1. return 语句。
  2. 抛出异常。

不管哪种返回方式都会导致栈帧被弹出。

2.3 本地方法栈

和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种错误。

2.4 堆

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

在 JDK 7 版本及JDK 7 版本之前,堆内存被通常被分为下面三部分:

  1. 新生代内存(Young Generation)
  2. 老生代(Old Generation)
  3. 永生代(Permanent Generation)

JDK 8 版本之后方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。

上图所示的 Eden 区、两个 Survivor 区都属于新生代(为了区分,这两个 Survivor 区域按照顺序被命名为 from 和 to),中间一层属于老年代。

大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

修正(issue552):“Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值”。

堆这里最容易出现的就是 OutOfMemoryError 错误,并且出现这种错误之后的表现形式还会有几种,比如:

  1. OutOfMemoryError: GC Overhead Limit Exceeded : 当JVM花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
  2. java.lang.OutOfMemoryError: Java heap space :假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfMemoryError: Java heap space 错误。(和本机物理内存无关,和你配置的内存大小有关!)

2.5 方法区

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

方法区也被称为永久代。很多人都会分不清方法区和永久代的关系,为此我也查阅了文献。

2.5.1 方法区和永久代的关系

《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。

2.5.3 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

  1. 整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

当你元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace

你可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。

  1. 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。

  2. 在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。

2.6 运行时常量池

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)

既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 错误。

JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

2.7 直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。

JDK1.4 中新加入的 NIO(New Input/Output) 类,引入了一种基于通道(Channel)缓存区(Buffer) 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据

本机直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。

三 HotSpot 虚拟机对象探秘

通过上面的介绍我们大概知道了虚拟机的内存情况,下面我们来详细的了解一下 HotSpot 虚拟机在 Java 堆中对象分配、布局和访问的全过程。

3.1 对象的创建

下图便是 Java 对象的创建过程,我建议最好是能默写出来,并且要掌握每一步在做什么。

Step1:类加载检查

虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

Step2:分配内存

类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式“指针碰撞”“空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定

内存分配的两种方式:(补充内容,需要掌握)

选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

内存分配并发问题(补充内容,需要掌握)

在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:

  • CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB: 为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配

Step3:初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

Step4:设置对象头

初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

Step5:执行 init 方法

在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

四 重点补充内容

4.1 String 类和常量池

String 对象的两种创建方式:

String str1 = "abcd";//先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"";
String str2 = new String("abcd");//堆中创建一个新的对象
String str3 = new String("abcd");//堆中创建一个新的对象
System.out.println(str1==str2);//false
System.out.println(str2==str3);//false

这两种不同的创建方法是有差别的。

  • 第一种方式是在常量池中拿对象;
  • 第二种方式是直接在堆内存空间创建一个新的对象。

记住一点:只要使用 new 方法,便需要创建新的对象。

String 类型的常量池比较特殊。它的主要使用方法有两种:

  • 直接使用双引号声明出来的 String 对象会直接存储在常量池中。
  • 如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。String.intern() 是一个 Native 方法,它的作用是:如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则返回常量池中该字符串的引用;如果没有,JDK1.7之前(不包含1.7)的处理方式是在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用,JDK1.7以及之后的处理方式是在常量池中记录此字符串的引用,并返回该引用。
       String s1 = new String("计算机");String s2 = s1.intern();String s3 = "计算机";System.out.println(s2);//计算机System.out.println(s1 == s2);//false,因为一个是堆内存中的 String 对象一个是常量池中的 String 对象,System.out.println(s3 == s2);//true,因为两个都是常量池中的 String 对象

字符串拼接:

       String str1 = "str";String str2 = "ing";String str3 = "str" + "ing";//常量池中的对象String str4 = str1 + str2; //在堆上创建的新的对象      String str5 = "string";//常量池中的对象System.out.println(str3 == str4);//falseSystem.out.println(str3 == str5);//trueSystem.out.println(str4 == str5);//false

尽量避免多个字符串拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。

4.2 String s1 = new String(“abc”);这句话创建了几个字符串对象?

将创建 1 或 2 个字符串。如果池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”。如果池中没有字符串常量“abc”,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。

验证:

     String s1 = new String("abc");// 堆内存的地址值String s2 = "abc";System.out.println(s1 == s2);// 输出 false,因为一个是堆内存,一个是常量池的内存,故两者是不同的。System.out.println(s1.equals(s2));// 输出 true

结果:

false
true

4.3 8 种基本类型的包装类和常量池

Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象。 为啥把缓存设置为[-128,127]区间?(参见issue/461)性能和资源之间的权衡。

public static Boolean valueOf(boolean b) {return (b ? TRUE : FALSE);
}
private static class CharacterCache {         private CharacterCache(){}static final Character cache[] = new Character[127 + 1];          static {             for (int i = 0; i < cache.length; i++)                 cache[i] = new Character((char)i);         }
}

两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

     Integer i1 = 33;Integer i2 = 33;System.out.println(i1 == i2);// 输出 trueInteger i11 = 333;Integer i22 = 333;System.out.println(i11 == i22);// 输出 falseDouble i3 = 1.2;Double i4 = 1.2;System.out.println(i3 == i4);// 输出 false

Integer 缓存源代码:

/**
*此方法将始终缓存-128 到 127(包括端点)范围内的值,并可以缓存此范围之外的其他值。
*/public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}

应用场景:

  1. Integer i1=40;Java 在编译的时候会直接将代码封装成 Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。
  2. Integer i1 = new Integer(40);这种情况下会创建新的对象。
  Integer i1 = 40;Integer i2 = new Integer(40);System.out.println(i1==i2);//输出 false

Integer 比较更丰富的一个例子:

  Integer i1 = 40;Integer i2 = 40;Integer i3 = 0;Integer i4 = new Integer(40);Integer i5 = new Integer(40);Integer i6 = new Integer(0);System.out.println("i1=i2   " + (i1 == i2));System.out.println("i1=i2+i3   " + (i1 == i2 + i3));System.out.println("i1=i4   " + (i1 == i4));System.out.println("i4=i5   " + (i4 == i5));System.out.println("i4=i5+i6   " + (i4 == i5 + i6));   System.out.println("40=i5+i6   " + (40 == i5 + i6));

结果:

i1=i2   true
i1=i2+i3   true
i1=i4   false
i4=i5   false
i4=i5+i6   true
40=i5+i6   true

解释:

语句 i4 == i5 + i6,因为+这个操作符不适用于 Integer 对象,首先 i5 和 i6 进行自动拆箱操作,进行数值相加,即 i4 == 40。然后 Integer 对象无法与数值进行直接比较,所以 i4 自动拆箱转为 int 值 40,最终这条语句转为 40 == 40 进行数值比较。

JVM基础知识和调优相关推荐

  1. 面了BAT,我总结了他们会问的JVM基础知识

    面试开始 一个一看就是一周没洗头,穿着格子衬衣的中年男子,拿着背面满是贴纸的 mac 走了进来.看着这地中海,心想他肯定是尼玛高级架构师吧!但是看过程序员大帝的小伙伴,肯定都跟无忌一样练就了吹的能力, ...

  2. 史上最全JVM整体架构和调优参数说明,带你彻底理解JVM整体架构与调优技巧

    大家好,我是冰河~~ 从今天开始,我们正式开始<架构师进阶系列>技术文的更新,在<架构师进阶系列>中,我们首先一起来探讨有关JVM的知识. 很多小伙伴都认为JVM的知识很难,很 ...

  3. JAVA面试题之JVM基础知识

    JAVA面试题总结-JVM的基础知识 JAVA面试题之JVM基础知识 说一下JVM的主要组成部分及作用 说一下 jvm 运行时数据区? 说一下堆和栈的区别? 队列和栈是什么?有什么区别? 什么是双亲委 ...

  4. JVM整体架构与调优参数说明

    本文来说下JVM整体架构与调优参数说明 文章目录 概述 JVM的分类 JVM的构成 方法区(元空间) 堆 栈 本地方法栈 程序计数器 JVM调优参数 本文小结 概述 很多小伙伴都认为JVM的知识很难, ...

  5. 由美团技术文章整理---spark性能优化基础篇--开发调优与资源参数调优

    文章地址1:Spark性能优化指南--基础篇 - 美团技术团队 文章地址2:Spark性能优化指南--高级篇 - 美团技术团队 目录 一.关于性能优化基础篇--开发调优 1.避免创建重复RDD (1) ...

  6. JVM常用参数以及调优实践

    JVM常用参数选项 jvm 可配置的参数选项可以参考 Oracle 官方网站给出的相关信息:http://www.oracle.com/technetwork/java/javase/tech/vmo ...

  7. 深入理解JVM虚拟机10:JVM常用参数以及调优实践

    本文转自[JVM常用参数以及调优实践](https://blog.csdn.net/a724888/article/details/78367780) JVM常用参数选项 jvm 可配置的参数选项可以 ...

  8. jvm原理及性能调优系列(jvm调优)

    jvm原理及性能调优系列(jvm调优) JVM设置: 1.设置合适的最大堆内存(新生代和老生代的最大和值)和最小堆内存(jvm启动时占用的操作系统内存大小),及设置好堆的比例分配. 2.设置合适的新生 ...

  9. jinfo java_Java自带的JVM性能监控及调优工具(jps、jinfo、jstat、jmap、javap)使用介...

    JVM介绍 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. ...

最新文章

  1. 华胜天成ivcs云系统初体验2
  2. 离开网易的转型之路1:选择测试之路-路上的迷茫
  3. 【收藏】在QGIS中添加Google Maps地图和卫星影像
  4. ssd网络结构_封藏的SSD(Single Shot MultiBox Detector)笔记
  5. javafx_JavaFX技巧4:总结
  6. python常用的库有哪些餐厅_这十个Python常用库,学习Python的你必须要知道!
  7. Ubuntu20.04 NAT 网络配置
  8. 计算机二级msoffice高级应用考试,全国计算机二级MSOffice高级应用考试大纲
  9. 年化收益17.1%!这个看基本面长线炒股的AI有点厉害
  10. pyplot设置刻度字体大小以及标签字体大小
  11. libevhtp介绍与demo构建
  12. 火狐 firefox proxy moz=proxy:// 407错误 解决办法
  13. 上传到服务器的图片访问的时候提示403 You don't have permission to access
  14. Elasticell-聊聊Raft的优化
  15. iphone163邮件服务器设置,怎样在iphone上设置网易免费企业邮箱收发邮件
  16. Android逆向Unity3D——XXX快跑破解
  17. U盘(电脑)文件夹变成exe(应用程序)怎么解决
  18. 长期在计算机房工作辐射大吗,在机房里工作辐射大吗
  19. arduino学习笔记十四--Arduino 环境光线传感器实验
  20. MAC主机eclipse连接parallels内win7虚拟机oracle

热门文章

  1. oracle 完全检查点条件,ORACLE Checkpoint(检查点)
  2. 数仓知识10_数据泛化
  3. uni-app使用uni-forms验证遇到的问题
  4. 南城故事---教你一招就能让安卓手机变得飞快!
  5. python在概率论与数理统计中的作用
  6. About g2o安装与使用
  7. HDU4006.The kth great number(优先队列)
  8. 深度学习磁共振图像超分与重建论文阅读
  9. jquery中解决设置图片不显示
  10. 蓝桥杯练习题六 - 大数乘法(c++)