什么是JVM

Java Virtual Machine,Java虚拟机

Java虚拟机有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统。

Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。

Java虚拟机不仅是一种跨平台的软件,而且是一种新的网络计算平台。该平台包括许多相关的技术,如符合开放接口标准的各种API、优化技术等。Java技术使同一种应用可以运行在不同的平台上。Java平台可分为两部分,即Java虚拟机(Java virtual machine,JVM)和Java API类库。

作用:JVM是java字节码执行的引擎,解析字节码文件的内容,并将其翻译为各种操作系统能理解的机器码

运作过程:解析字节码文件的时候,会先加载字节码文件,将其存入java虚拟机的内存空间中,进行一系列的动作,最后运行程序得出结果

JVM内存空间

字节码数据在java虚拟机内存中如何存放的?

虚拟机栈

用于执行java方法,每个方法执行都会创建一个栈帧,存储局部变量表,操作数栈,动态链接等信息,程序执行时,栈帧入栈,执行完成后栈帧出栈

程序计数器

或者叫PC寄存器,记载着每一个线程当前运行的JAVA方法的地址,指示当前程序执行到了哪个位置:

1、执行Java方法时记录正在执行的虚拟机字节码指令地址;
2、执行本地方法时,计数器值为null;

每一个线程都有一个PC寄存器,也就是说PC寄存器是线程独有的。
生命周期跟随线程,线程启动而产生,线程结束而消亡。
是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。

本地方法区

如果程序有使用到native方法,加载和执行就在这个区域。

方法区

存储 Java 类字节码数据的一块区域,存储类,常量相关的信息。

Java堆

Java虚拟机管理的内存中最大的一块,Java虚拟机启动的时候建立,所有线程共享,几乎所有的对象实例都在这里分配内存。

GC主要就是在Java堆中进行的。

Java 堆根据对象存活时间的不同,Java 堆还被分为新生代(或叫 年轻代)、老年代两个区域,新生代还被进一步划分为 Eden 区、Survivor 0、Survivor 1 区。

新生代

  1. 新生代内存按照8:1:1的比例分为一个Eden区和两个survivor区(survivor0和survivor1);
  2. 当有对象需要分配时,优先被分配在年轻代的 Eden 区;
  3. 当Eden区满了,就需要进行垃圾回收(Minor GC),回收 Eden 区中没有被引用的对象(可达性分析);
  4. 在年轻代的对象经过了指定次数(默认15次)的 GC 后,将在下次 GC 时进入老年代;
  5. 两个survivor区(survivor0和survivor1)

老年代

  1. 老年代存放的都是一些生命周期较长的对象,或者是很大的对象(大于Eden区空间);
  2. 默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小,老年代 ( Old ) = 2/3 的堆空间大小;
  3. 将对象从新生代移到老年代时,如果此时老年代空间不够,那么触发 Major GC。

GC介绍

GC,即垃圾回收
英语:Garbage Collection,缩写为GC

GC是一种自动的内存管理机制

垃圾回收的术语:

Minor GC
从新生代空间回收内存被称为 Minor GC,有时候也称之为 Young GC。

Major GC
从老年代空间回收内存被称为 Major GC,有时候也称之为 Old GC。

Full GC
Full GC 是清理整个堆空间 —— 包括新生代、老年代。因此 Full GC 可以说是 Minor GC 和 Major GC 的结合。

Stop-The-World
翻译为全世界暂停,简称 STW,是指在进行垃圾回收时,因为标记或清理的需要,必须让所有执行任务的线程停止执行任务,从而让垃圾回收线程完成回收垃圾的时间。

垃圾回收机制的重点:

哪些内存需要回收?

垃圾收集器会对堆进行回收前,确定对象中哪些是“存活”,哪些是“死亡”(不可能再被任何途径使用的对象);

当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。

举例:一颗树有很多丫枝,其中一个分支断了,跟树上没有任何联系,那就说明这个分支没有用了,就可以当垃圾回收去烧了。

什么时候回收?

主要的场景:
(1)JVM 无法为一个新的对象分配空间时会触发 Minor GC

(2)老年代空间不够,那么触发 Major GC

(3)当准备要触发一次Minor GC时,如果出现历史Minor GC的平均晋升大小比目前 “老年代”剩余的空间大,老年代剩余空间不够用于新生代晋升,则不会触发young GC,而是转为触发full GC

如何回收?

垃圾收集算法:

1)标记—清除算法

标记—清除算法(Mark-Sweep),是最基础的收集算法,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程是通过可达性分析算法实现的。

回收前状态

回收后状态

算法缺点:
标记和清除过程的效率都不高;标记清除后会产生大量不连续的内存碎片。

2)复制算法

复制算法将可用内存按容量分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面,然后再把已使用过的内存空间一次清理掉。

回收前状态

回收后状态

复制算法优点:
每次只对一块内存进行回收,运行高效;大大减少了内存碎片的出现;

缺点:
可初始分配的最大内存缩小了一半;

3)标记—整理算法

分为“标记”、“整理”、“清除”三个阶段
先对内存区域的对象进行标记,区分出“存活对象”和“可回收对象”,
让所有的对象都向内存区域一端移动,直接清理掉可回收的对象;

回收前状态

回收后状态

4)分代回收

根据对象的存活周期的不同,将内存划分为新生代和老年代。

在新生代中,每次垃圾收集时都会发现有大量对象死去,只有少量存活,因此可选用复制算法来完成收集;

新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块比较大的Eden空间和两块较小的Survivor空间(8:1:1的比例),每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。

每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的空间会被预留。

老年代中因为对象存活率高、没有额外空间对它进行分配担保,就使用标记—清除算法或标记—整理算法来进行回收

新生代回收

JVM常用参数

Jvm启动参数共分为三类:

一、是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;

二、是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;

三、是非Stable参数(-XX),此类参数各个jvm实现会有所不同;

-Xms256m
为jvm启动时分配的内存-Xmx256m
为jvm运行过程中分配的最大内存,建议和Xms保持一致-Xmn128m
设置年轻代大小为128M
此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。-Xss256k
为jvm启动的每个线程分配的内存大小-XX:NewSize 和-XX:MaxNewSize
用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。-XX:PermSize=1024M 和 -XX:MaxPermSize=1024M
JVM初始分配的非堆内存, 不会被回收, 建议与maxPermSize相同-XX:SurvivorRatio=4
年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。
设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6-XX:NewRatio=4
设置年轻代(EC+S0C+S1C)和年老代(OC)的比值。如:为4,表示年轻代与年老代比值为1:4,年轻代占整个年轻代年老代和的1/5-XX:InitialTenuringThreshol 和-XX:MaxTenuringThreshold
用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1。-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-Xloggc:../logs/gc.log 日志文件的输出路径-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=20M
JVM的一个日志文件达到了20M以后,就会写入另一个新的文件,最多会有5个日志文件,他们的名字分别是:gc.log.0、gc.log.1 等 -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息-XX:+PrintTenuringDistribution
这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。2020-06-29T03:45:25.512+0800: 76.642: [GC pause (G1 Evacuation Pause) (young)
Desired survivor size 402653184 bytes, new threshold 2 (max 15)
- age   1:  169351248 bytes,  169351248 total
- age   2:  331023392 bytes,  500374640 total
- age   3:   64201192 bytes,  564575832 total
, 1.2957512 secs]-XX:MaxTenuringThreshold
用于调整对象晋升老年代的所需经历的GC次数,默认15次,即在年轻代的对象经过了指定次数的 GC 后,将在下次 GC 时进入老年代。

JVM调优浅谈

JVM内存管理很差会出现什么情况?

1,内存溢出
2,频繁Full GC
….

内存溢出:

  1. OutOfMemoryError: Java heap space 堆溢出

    最常见的内存溢出,当在JVM中如果98%的时间是用于GC且可用的 Heap size 不足2%的时候将抛出此异常信息。

    优化建议:

    Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为3/8的-Xmx值。

  2. OutOfMemoryError: PermGen space 非堆溢出(永久保存区域溢出)

    这块内存主要是被JVM存放Class和Meta信息的,是 JVM初始分配的非堆内存,没有GC回收,一般发生在程序的启动阶段。

    优化建议:

    通过-XX:PermSize和 -XX:MaxPermSize设置合适的内存大小

  3. OutOfMemoryError: unable to create new native thread. 无法创建新的线程

    常见在高并发的业务场景,线程池一直新建线程,每个线程都有自己的Stack Space,Stack Space的空间是独立分配的,当超出jvm的栈内存大小时, 就会报出无法再创建线程的错误.

    优化建议:

    设置合适的-Xss参数,优化线程池资源分配。

  4. java.lang.StackOverflowError : Thread Stack space

    栈溢出了,常见于代码递归的层次过多。

    优化建议:

    修改程序、设置合适的-Xss参数。

MinorGC 日志

2020-06-23T04:35:59.604+0800: 463.092: [GC (Allocation Failure)
2020-06-23T04:35:59.604+0800: 463.092: [ParNew: 43296K->7006K(47808K), 0.0136826 secs]
44992K->8702K(252608K), 0.0137904 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 2020-06-23T04:35:59.604+0800  日志文件的时间戳463.092  JVM记录的时间戳:GC开始,相对JVM启动的相对时间,单位是秒GC  触发了YoungGC/MinorGCAllocation Failure – MinorGC的原因,由于年轻代不满足申请的空间,因此触发了MinorGC[ParNew(使用ParNew作为新生代的垃圾回收器,采用的是复制算法): 43296K(年轻代垃圾回收前的大小)->7006K(年轻代垃圾回收以后的大小)(47808K)
(年轻代的总大小), 0.0136826 secs(回收时间)] 44992K(堆区垃圾回收前的大小)->8702K(堆区垃圾回收后的大小)(252608K)(堆区总大小), 0.0137904 secs(回收时间)][Times: user=0.03 sys=0.00, real=0.02 secs]
user:GC 线程在垃圾收集期间所使用的 CPU 总时间;
sys:系统调用或者等待系统事件花费的时间;
real:应用被暂停的时钟时间,由于 GC 线程是多线程的,导致了 real 小于 (user+real),如果是 gc 线程是单线程的话,real 是接近于 (user+real) 时间。

FullGC 日志

2018-04-12T13:48:26.233+0800: 15578.148: [GC [1 CMS-initial-mark: 6294851K(20971520K)]
6354687K(24746432K), 0.0466580 secs] [Times: user=0.04 sys=0.00, real=0.04 secs]
2018-04-12T13:48:26.280+0800: 15578.195: [CMS-concurrent-mark-start]
2018-04-12T13:48:26.418+0800: 15578.333: [CMS-concurrent-mark: 0.138/0.138 secs]
[Times: user=1.01 sys=0.21, real=0.14 secs]
2018-04-12T13:48:26.418+0800: 15578.334: [CMS-concurrent-preclean-start]
2018-04-12T13:48:26.476+0800: 15578.391: [CMS-concurrent-preclean: 0.056/0.057 secs]
[Times: user=0.20 sys=0.12, real=0.06 secs]
2018-04-12T13:48:26.476+0800: 15578.391: [CMS-concurrent-abortable-preclean-start]
2018-04-12T13:48:29.989+0800: 15581.905: [CMS-concurrent-abortable-preclean: 3.506/3.514 secs]
[Times: user=11.93 sys=6.77, real=3.51 secs]
2018-04-12T13:48:29.991+0800: 15581.906: [GC[YG occupancy: 1805641 K (3774912 K)]
2018-04-12T13:48:29.991+0800: 15581.906: [GC2018-04-12T13:48:29.991+0800: 15581.906:
[ParNew: 1805641K->48395K(3774912K), 0.0826620 secs] 8100493K->6348225K(24746432K),
0.0829480 secs] [Times: user=0.81 sys=0.00, real=0.09 secs]2018-04-12T13:48:30.074+0800:
15581.989: [Rescan (parallel) , 0.0429390 secs]2018-04-12T13:48:30.117+0800: 15582.032:
[weak refs processing, 0.0027800 secs]2018-04-12T13:48:30.119+0800: 15582.035: [class
unloading, 0.0033120 secs]2018-04-12T13:48:30.123+0800: 15582.038: [scrub symbol table,
0.0016780 secs]2018-04-12T13:48:30.124+0800: 15582.040: [scrub string table, 0.0004780
secs] [1 CMS-remark: 6299829K(20971520K)] 6348225K(24746432K), 0.1365130 secs]
[Times: user=1.24 sys=0.00, real=0.14 secs]
2018-04-12T13:48:30.128+0800: 15582.043: [CMS-concurrent-sweep-start]
2018-04-12T13:48:38.412+0800: 15590.327: [CMS-concurrent-sweep: 8.193/8.284 secs]
[Times: user=30.34 sys=16.44, real=8.28 secs]
2018-04-12T13:48:38.419+0800: 15590.334: [CMS-concurrent-reset-start]
2018-04-12T13:48:38.462+0800: 15590.377: [CMS-concurrent-reset: 0.044/0.044 secs]
[Times: user=0.15 sys=0.10, real=0.04 secs]
阶段1:Initial Mark
2018-04-12T13:48:26.233+0800: 15578.148: [GC [1 CMS-initial-mark: 6294851K(20971520K)]
6354687K(24746432K), 0.0466580 secs] [Times: user=0.04 sys=0.00, real=0.04 secs]CMS-initial-mark:初始标记阶段,CMS是老年代垃圾回收器,基于标记-清除算法实现,
它会收集所有 GC Roots 以及其直接引用的对象;
6294851K:当前老年代使用的容量,这里是 6G;
(20971520K):老年代可用的最大容量,这里是 20G;
6354687K:整个堆目前使用的容量,这里是 6.06G;
(24746432K):堆可用的容量,这里是 23.6G;
0.0466580 secs:这个阶段的持续时间;这个是 CMS 两次 stop-the-wolrd 事件的其中一次,这个阶段的目标是:标记那些直接被 GC root 引用
或者被年轻代存活对象所引用的所有对象
阶段2:CMS-concurrent-mark
2018-04-12T13:48:26.280+0800: 15578.195: [CMS-concurrent-mark-start]
2018-04-12T13:48:26.418+0800: 15578.333: [CMS-concurrent-mark: 0.138/0.138 secs]
[Times: user=1.01 sys=0.21, real=0.14 secs]CMS-concurrent-mark:并发标记阶段,遍历老年代,标记所有存活的对象,由第一阶段标记过的对象出发,
所有可达的对象都在本阶段标记;
0.138/0.138 secs:这个阶段的持续时间与时钟时间;2018-04-12T13:48:26.418+0800: 15578.334: [CMS-concurrent-preclean-start]
2018-04-12T13:48:26.476+0800: 15578.391: [CMS-concurrent-preclean: 0.056/0.057 secs]
[Times: user=0.20 sys=0.12, real=0.06 secs]
阶段3:Concurrent Preclean
Concurrent Preclean :并发预清理阶段,对在前面并发标记阶段中引用发生变化的对象进行标记,
包含:从新生代晋升、新分配、被更新的对象,并发地重新扫描这些对象;
0.056/0.057 secs:这个阶段的持续时间与时钟时间;2018-04-12T13:48:26.476+0800: 15578.391: [CMS-concurrent-abortable-preclean-start]
2018-04-12T13:48:29.989+0800: 15581.905: [CMS-concurrent-abortable-preclean:
3.506/3.514 secs] [Times: user=11.93 sys=6.77, real=3.51 secs]
阶段4:Concurrent Abortable Preclean
Concurrent Abortable Preclean :并发可中止的预清理阶段,和上一阶段工作内容一样,但可以控制结束
时间,这个阶段持续时间依赖于很多的因素:完成的工作量、扫描持续时间等;2018-04-12T13:48:29.991+0800: 15581.906: [GC[YG occupancy: 1805641 K (3774912 K)]
2018-04-12T13:48:29.991+0800: 15581.906: [GC2018-04-12T13:48:29.991+0800: 15581.906:
[ParNew: 1805641K->48395K(3774912K), 0.0826620 secs] 8100493K->6348225K(24746432K),
0.0829480 secs] [Times: user=0.81 sys=0.00, real=0.09 secs]
2018-04-12T13:48:30.074+0800: 15581.989: [Rescan (parallel) , 0.0429390 secs]
2018-04-12T13:48:30.117+0800: 15582.032: [weak refs processing, 0.0027800 secs]
2018-04-12T13:48:30.119+0800: 15582.035: [class unloading, 0.0033120 secs]
2018-04-12T13:48:30.123+0800: 15582.038: [scrub symbol table, 0.0016780 secs]
2018-04-12T13:48:30.124+0800: 15582.040: [scrub string table, 0.0004780 secs]
[1 CMS-remark: 6299829K(20971520K)] 6348225K(24746432K), 0.1365130 secs]
[Times: user=1.24 sys=0.00, real=0.14 secs]
阶段5:remark
remark 重标记阶段重标记阶段(CMS的第二个STW阶段),暂停所有用户线程,从GC Root开始重新扫描整堆,标记存活的对象。
虽然CMS只回收老年代的垃圾对象,但是这个阶段依然需要扫描新生代,因为很多GC Root都在新生代,
而这些GC Root指向的对象又在老年代,这称为“跨代引用”。YG occupancy: 1805641 K (3774912 K):年轻代当前占用量及总容量,这里分别是 1.71G 和 3.6G;
ParNew:触发了一次 young GC,原因是为了减少年轻代的存活对象,尽量使年轻代更干净一些;
[Rescan (parallel) , 0.0429390 secs]:这个 Rescan 是当应用暂停的情况下完成对所有存活对象的标记,
这个阶段是并行处理的,这里花费了 0.0429390s;
[weak refs processing, 0.0027800 secs]:第一个子阶段,它的工作是处理弱引用;
[class unloading, 0.0033120 secs]:第二个子阶段,它的工作是:unloading the unused classes;
[scrub symbol table, 0.0016780 secs]、[scrub string table, 0.0004780 secs]:
最后一个子阶段,它的目的是:cleaning up symbol and string tables which hold class-level
metadata and internalized string respectively
CMS-remark:remark结束,输出当前老年代的使用量与总量6299829K(20971520K),
堆的使用量与总量6348225K(24746432K)2018-04-12T13:48:30.128+0800: 15582.043: [CMS-concurrent-sweep-start]
2018-04-12T13:48:38.412+0800: 15590.327: [CMS-concurrent-sweep: 8.193/8.284 secs]
[Times: user=30.34 sys=16.44, real=8.28 secs]
阶段6:并发清理阶段

这个阶段主要是清除那些没有被标记的对象,回收它们的占用空间;这里不需要 STW,
它是与用户的应用程序并发运行。2018-04-12T13:48:38.419+0800: 15590.334: [CMS-concurrent-reset-start]
2018-04-12T13:48:38.462+0800: 15590.377: [CMS-concurrent-reset: 0.044/0.044 secs]
[Times: user=0.15 sys=0.10, real=0.04 secs]
阶段7:Concurrent Reset阶段
这个阶段也是并发执行的,它会重设 CMS 内部的数据结构,为下次的 GC 做准备。

Full GC频繁发生怎么办?

排查方向:
1、根据FULLGC日志分析,消耗在什么阶段;
2、JVM参数设置不合理;
3、是否有内存泄露;
4、对象的内存分配存在问题,大对象过多;
5、使用jstat、jstack、jmap命令定位;

【JVM调优】JVM内存管理调优浅谈相关推荐

  1. Spark调优:提交job资源参数调优及内存模型调优

    [场景] Spark提交作业job的时候要指定该job可以使用的CPU.内存等资源参数,生产环境中,任务资源分配不足会导致该job执行中断.失败等问题,所以对Spark的job资源参数分配调优非常重要 ...

  2. 关于Linux性能调优之内存负载调优

    写在前面 整理一些Linux内存调优的笔记,分享给小伙伴 博文没有涉及的Demo,理论方法偏多,可以用作内存调优入门 博文内容涉及: Linux内存管理的基本理论 寻找内存泄露的进程 内存交换空间调优 ...

  3. Java跨平台实现原理及JVM垃圾回收、内存管理实战

    对象已死?啊,难受-- 最近深陷排查各种内存溢出.内存泄漏的问题,不得不对垃圾回收器下手了,因为当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些"自动化"的技术实施必要的 ...

  4. 内存分段分页机制理解_深入理解虚拟机,JVM高级特性-自动内存管理机制

    什么是自动内存管理机制? 对于java程序员来说,有一点是要比C/C++程序员要方便的,那就是程序在运行时,java程序不需要为每一个对象其编写对应的释放内存的代码,JVM虚拟机将为你在合适的时间去释 ...

  5. 细说JVM系列:自动内存管理内存回收:垃圾收集理论-垃圾收集算法

    垃圾收集理论-垃圾收集算法 这里主要讲解垃圾收集理论上的算法,下一篇会介绍一些实现了这些算法的垃圾收集器. 一般我们谈垃圾收集从三个问题来帮你理解jvm的垃圾收集策略: 1.怎么判断哪些内存是垃圾? ...

  6. java内存模型浅析_浅谈java内存模型

    不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的.其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结jav ...

  7. mysql事务的管理方式_浅谈MySQL事务管理(基础)

    本篇文章给大家带来的内容是浅谈MySQL事务管理(基础),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助.事务处理用来维护数据库等完整性,保证mysql操作要么成功,要么失败(myisa ...

  8. 服务器不知别内存_程序优化浅谈服务器实现高并发的原理

    我们每天刷手机都会接收到各种各样的信息,看到这篇文章时,不知大家有没有思考过,这些信息是如何组织,然后通过服务器发送给我们的呢? 其实也很简单,不就是一条一条请求嘛,服务器根据不同的请求分别去不同的数 ...

  9. 【JVM学习】Java内存管理一

    概述 本文将结合自身对JVM的学习,对java虚拟机内存的各个区域阐述个人理解. 首先我们需要了解的是,JVM是<java虚拟机规范>定义的一种规范或标准,而我们在实际应用当中要讨论的是落 ...

最新文章

  1. iOS 9 学习系列: Xcode Code Coverage
  2. lnmp一键包502 Bad Gateway解决方法
  3. org.apache.jasper.JasperException: Unable to co...
  4. 中专选计算机应用很难,对中专计算机应用基础改革的思考.pdf
  5. 小米架构调整:将销售与服务部改组为中国区 王川任总裁
  6. SLAM学习与求职经验分享_李阳阳
  7. 免费思维导图工具推荐,办公必备
  8. IT战略规划怎样做得更务实
  9. 从零开始设计一款APP之如何做原型图
  10. 开源看板 wekan docker-compose部署
  11. 搜索引擎Bing必应高级搜索技巧
  12. docker安装mssql
  13. 泛函分析笔记0:绪论
  14. 计算机设备管理器无com,计算机无设备管理器的方法
  15. 不歧视双非的计算机院校,这30所重点院校不歧视“双非”,公平竞争录取,爱了爱了!...
  16. 上海高考物理能不能用计算机,上海高考物理卷并未超纲 但不按套路出牌
  17. 信不信由你,反正我是信了!接龙啊。。。。。
  18. 说出至少4种vue当中的指令和它的用法?
  19. 《Presto(Trino)——The Definitive Guide》CHAPTER 6 Connectors Advanced CHAPTER 7 Connector Examples
  20. 向日葵 服务器连接失败怎么办

热门文章

  1. 更改Linux用户的登录shell环境
  2. ViewPager之引导界面---实现欢迎引导页面
  3. 二分匹配(匈牙利算法)模板
  4. golang中的redigo
  5. golang查找重复行
  6. apache服务器的配置文件httpd.conf中有很多内容,请解释如下配置项:
  7. rabbitmq一:基本概念
  8. Linux下Tomcat重新启动以及日志
  9. 剑指offer一:二维数组中的查找
  10. 实验1 C语言开发环境使用和数据类型、运算符、表达式