本章小宋带大家复习一下JVM的内存区域,下面没有特别说明的话,都是在说HotSpot虚拟机。不耽搁大家的时间了,直接进入今天JVM的学习时间吧。

Java内存区域

  • 简述
    • JVM和HotSpot的联系
  • 运行时数据区域
    • 程序计数器
    • 虚拟机栈
    • 本地方法栈
    • 方法区
      • 方法区和永久代的关系
      • 永久代常用参数
      • 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
    • 运行时常量池
    • 直接内存
  • HotSpot虚拟机对象
    • 对象的创建
      • 1.类加载检查
      • 2.分配内存
      • 3.初始化零值
      • 4.设置对象头
      • 5.执行init方法
    • 对象的内存布局
    • 对象的访问定位
  • String类和常量池
  • String s1 = new String("abc");这句话创建了几个字符串对象?

简述

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

JVM和HotSpot的联系

JVM是虚拟机,总的来说是一种标准规范,虚拟机有很多实现版本,主要作用就是运行java的类文件的。

而HotSpot是虚拟机的一种实现,它是sun公司开发的,是sun jdk和open jdk中自带的虚拟机,同时也是目前使用范围最广的虚拟机。
HotSpot,顾名思义,它是基于热点代码探测的,有JIT即时编译功能,能提供更高质量的本地代码。

二者区别是一个是标准,一个是实现方式。

简单来说就是这一样层关系:JDK是java开发环境,JRE是java运行环境,JDK包含JRE,而JRE包含JVM,HotSpot是JVM的实现。

运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。

JDK1.8之前(1.6)如下图所示:

JDK1.8如下图所示:

通过上面的图我们知道;
线程私有的: 虚拟机栈,本地方法栈,程序计数器
线程共享的:堆,方法区,直接内存(非运行时内存一部分)

然后在1.8的时候方法区的实现永久代被元空间替换。

程序计数器

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

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

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

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

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

虚拟机栈

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

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

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

Java 虚拟机会出现两种错误:StackOverFlowErrorOutOfMemoryError

  • StackOverFlowError: 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
  • OutOfMemoryError: 若 Java 虚拟机堆中没有空闲内存,并且垃圾回收器也无法提供更多内存的话。就会抛出 OutOfMemoryError 错误。

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

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

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

Java 方法有两种返回方式:

return 语句。
抛出异常。
不管哪种返回方式都会导致栈帧被弹出。

本地方法栈

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

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

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

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

Java世界中“几乎”所有的对象都在堆中分配,但是,随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。从jdk 1.7开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。

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 来设置。

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

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {//survivor_capacity是survivor空间的大小size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);size_t total = 0;uint age = 1;while (age < table_size) {total += sizes[age];//sizes数组是每个年龄段对象大小if (total > desired_survivor_size) break;age++;}uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;...
}

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

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

方法区

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

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

方法区和永久代的关系

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

永久代常用参数

JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参数来调节方法区大小

-XX:PermSize=N //方法区 (永久代) 初始大小
-XX:MaxPermSize=N //方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。

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

下面是一些常用参数:

-XX:MetaspaceSize=N //设置 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //设置 Metaspace 的最大大小

与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。

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

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

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

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

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

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

运行时常量池

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

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

JDK1.7以前到1.8运行时常量池的位置变化:

  1. JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
  2. JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代 。
  3. JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)

直接内存

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

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

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

HotSpot虚拟机对象

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

对象的创建

如下图所示(创建对象过程):

1.类加载检查

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

2.分配内存

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

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


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

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

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

3.初始化零值

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

4.设置对象头

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

5.执行init方法

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

对象的内存布局

在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:对象头实例数据对齐填充

Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪
个类的实例。

实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。

对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,换句话说就是对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对象的访问定位

建立对象就是为了使用对象,我们的 Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有①使用句柄和②直接指针两种:

  1. 句柄:如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息;
  2. 直接指针: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象的地址。

    这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。

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类型的常量池较特殊,有两种方式使用方式:

  1. 直接使用双引号声明出来的 String 对象会直接存储在常量池中。
  2. 如果不是用双引号声明的 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);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false


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

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

讲到这里本章对 JVM内存区域的相关知识讲解也就结束了,如果想了解更多知识可以在对应的专栏中看系列文章,谢谢大家的观看,希望能给各位同学带来帮助。如果觉得博主写的还可以的,可以点赞收藏。

缥缈峰--JVM系列之内存区域相关推荐

  1. 【JVM】Java内存区域详解(通俗易懂系列)

    Java虚拟机拥有管理内存的权利. 一.运行时数据区 在Java程序执行的过程中,Java虚拟机会将它管理的内存分为若干个不同数据区域(JDK1.8与之前版本不同) 线程私有: 虚拟机栈 本地方法栈 ...

  2. 深入理解JVM(1)——Java内存区域与Java对象

    最近放暑假在家,没有实验室的工作要做,正好趁这个时间恶补了之前一知半解的JVM知识,使用的主要学习资料是周志明大大的<深入理解Java虚拟机--JVM高级特性与最佳实践>,我仔细拜读了两遍 ...

  3. 学习jvm(一)--java内存区域

    前言 通过学习深入理解java虚拟机的教程,以及自己在网上的查询的资料,做一个对jvm学习过程中的小总结. 本文章内容首先讲解java的内存分布区域,之后讲内存的分配原则以及内存的监控工具.再下来会着 ...

  4. jvm(2)-java内存区域

    [0]README 0.1)本文转自 深入理解jvm, 旨在学习 java内存区域 的基础知识: [1]运行时数据区域 1)jvm 所管理的内存将会包括以下几个运行时数据区域 1.1)方法区:(线程共 ...

  5. 【JVM】JVM-codecache内存区域介绍

    1.概述 转载:https://leokongwq.github.io/2016/10/12/maven-test.html 2.JVM-codecache内存区域介绍 大家都知道JVM在运行时会将频 ...

  6. 深入理解JVM之Java内存区域与内存溢出异常

    读了<深入理解Java虚拟机-JVM高级特性与最佳实践>的第二章,明白了在虚拟机中内存是如何划分的,哪部分区域.什么样的代码和操作可能导致内存溢出异常.虽然Java有垃圾收集机制,但是内存 ...

  7. jvm之java内存区域详解篇guide哥yyds

    jvm 一.java内存区域详解 1.运行时数据区域 线程私有的: 虚拟机栈 本地方法栈 程序计数器 线程共享的: 堆 方法区 直接内存(非程序运行时数据区的一部分) 1.1什么是程序计数器 程序计数 ...

  8. JVM总结之内存区域详解

    一.概述 1 运行时数据区概述 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域.JDK 1.8 和之前的版本略有不同,下面会介绍到. 线程私有的: 程序计数器 ...

  9. jvm有哪些内存区域

    1.程序计数器 程序计数器是一块很小的内存空间,它是线程私有的,可以认作为当前线程的行号指示器. 那么计数器记录虚拟机字节码指令的地址.如果为native[底层方法],那么计数器为空. 这块内存区域是 ...

  10. jvm中有哪些内存区域会发生内存溢出

    1.方法区溢出 第一种原因:上线的时候MetaSpace区域直接用默认的参数,即根本不设置其大小,这会导Meta Space区域可能才几十M而已或者设置的比较小,此时对于一个稍微大型系统,因为他有很多 ...

最新文章

  1. kernel logo到开机动画之间闪现黑屏(android 5.X)
  2. win32 debug api 原理
  3. PHP 5.6.32 增加pdo_dblib.so拓展
  4. HDFS多用户管理ACL机制other权限访问控制的理解
  5. 【11_83】Remove Duplicates from Sorted List
  6. 根据上边栏和下边栏的高度进行布局
  7. 3000米排名预测(全排列+判断)
  8. SOA架构设计经验分享—架构、职责、数据一致性
  9. 第十四节(接口(行为))
  10. 如何让cloudflare缓存html,wordpress博客使用CloudFlare的页面规则缓存设置教程
  11. 一步一步写算法(之prim算法 下)
  12. 通过新浪云部署NideShop微信小程序商城(基于Node.js+MySQL+ThinkJS)
  13. Cherry,你会喜欢这种手机!
  14. 二阶齐次线性微分方程的通解公式_2016考研数学:二阶常系数齐次线性微分方程的通解证明...
  15. 300万高清车牌识别一体机Http推送
  16. AXure破解授权码
  17. 01web前端笔试试题
  18. java 多次正则匹配_Java正则多次匹配和多次组匹配
  19. linux log原理,linux系统日志管理工具logrotate之原理详述
  20. 运维工程师故障排查思路(备忘)

热门文章

  1. GEEer成长日记二:Modis_NDVI逐日和逐月时间序列分析
  2. 北卡州立大学计算机科学硕士,2020年北卡罗来纳州立大学一年制硕士含金量
  3. 2021宇哥八套卷总结—第三套试卷分析
  4. 「开发者说」钉钉连接平台实现OA审批与企业内部系统数据互通
  5. 泛函分析 01.01 距离空间-绪论
  6. 一文读懂增强现实(AR)技术
  7. Echart甘特图实现效果 + 配带抽成方法 + 源码
  8. java hl7v3_HL7标准V3开发框架中个模型的关系
  9. ATX电源的基本描述
  10. Unity TalkingData接入