jvm

1 介绍

定义:java virtual meachine -java运行时环境(java二进制字节码的运行环境)。

好处:

  • 一次编写到处运行
  • 自动内存管理,垃圾回收
  • 数组下标越界检查
  • 多态
jvm组成部分

2. 内存结构

2.1 程序计数器(线程私有)

作用: 记住下一条jvm指令的执行地址。

由于现实中程序往往是多线程协作完成任务的。JVM的多线程是通过 CPU时间片轮转 来实现的,某个线程在执行的过程中可能会因为时间片耗尽而挂起。当它再次获取时间片时,需要从挂起的地方继续执行。在JVM中,通过程序计数器来记录程序的字节码执行位置。程序计数器具有线程隔离性,每个线程拥有自己的程序计数器。

特点:

  • 线程私有
  • 内存中唯一一个不会出现内存溢出的区域
image.png

2.2 虚拟机栈

定义: Java Virtual Mechine Stacks(java虚拟机栈)

  • 每个线程运行时所需要的内存空间
  • 每个栈由多个栈帧(可能存放参数,局部变量,返回值等信息)组成,对应着该线程调用方法占用的内存
  • 每个线程都只有一个活动栈帧,对应着线程当前正在执行的方法。

问题

  • 垃圾回收是否涉及到栈内存?
    答: 不会涉及。因为栈对应着一个程序的执行过程,在执行过程中,活动栈不断随着该方法的运行结束而弹出,直到程序执行完成,栈为空,消失。即一个进程执行完毕,线程栈消失。
  • 栈内存越大越好吗?
    答: 不是。服务器物理内存是固定的,若栈内存越大,则能够同时运行的线程数量就会减小。栈是由多个栈帧组成,内存增大,只是将可以容纳栈帧的数量增多,除了可以调用更多的方法(递归),没有其他的作用。
    -Xss设置栈内存大小,一般-Xss=1M。
  • 线程的局部变量是否线程安全?
    答: 不一定。方法内的局部变量没有逃离方法的作用范围时,是线程安全的。如果局部变量引用了对象,由于对象存在于堆中,一般其他线程可以访问修改,需要考虑线程安全。

栈内存溢出

  • 栈帧过多,栈被撑破了(递归,结束条件有问题,不停的调用自己,导致栈帧过多)
  • 栈帧过大(交叉引用)

线程诊断

  • cpu占用过多
    1.定位进程,top找出占用cpu过多的进程PID
    2.ps -H -eo pid,tip,%cpu | grep PID 找出该进程下占用过大的线程 TID
    3.jstack pid 查看进程中各线程的详细信息,可以看出问题所在。注意:这里TID为十六进制,需要讲上一步ps查出来的TID转换为16进制。
jstack显示Demo1_16.java的第8行有问题
  • 运行迟迟没结果
    jstack pid 查看进程中各线程的详细信息,拉到最后,发现死锁问题。
死锁问题

2.3 本地方法栈(线程私有)

本地方法栈类似于虚拟机栈,也是线程私有。
不同点:本地方法栈服务的对象是jvm运行的native方法,而虚拟机栈服务的是jvm执行的java方法。

2.4 堆(线程共享)

定义: Heap,通过new关键字创建的对象,都存放在堆内存中。
特点:

  • 线程共享,堆中的对象都存在线程安全的问题
  • 垃圾回收,垃圾回收机制重点区域。

堆内存溢出
java.lang.OutOfMemeryError:Java Heap space

以下示例在程序执行过程中,由于a字符串不断地累加,并且往list中添加导致list规模越来越大,垃圾回收机制认为它是有用的,所以不会被回收,最终造成堆内存溢出:

image.png

堆内存诊断

  • jps
    查看系统有哪些进程。
  • jmap
    查看某一时刻堆内存使用情况 jmap -heap PID
  • jconsole
    图形界面,多功能检测工具,连续监测。直接在控制台输入jconsole,选择连接想要查看的PID。
  • jvistualvm
    图形界面,多功能检测工具,可以直接定位到内存占用过高的位置。

一个简单的案例:

public class Demo1 {
    public static  void main(String[] args) throws InterruptedException {
        List<student> list = new ArrayList<>();
        for (int i = 0; i < 200;i++){
            list.add(new student());
        }
        Thread.sleep(10000000000L);
    }
}

class student{
    private byte[] big = new byte[1024 * 1024];
}
  1. 打开jvistualvm,在1处选择对应进程,2处选监视,3处点“dump”。
image.png
  1. 点击“查找”,点击第一条占用内存最大的记录。
image.png
  1. 找到问题所在,list中有过多大对象student,无法被清除。

2.5 方法区(线程共享)

定义: 主要存储class文件的信息和运行时常量池,class文件的信息包括类信息和class文件常量池。

组成与实现

jdk6(永久代实现)和jdk8(元空间实现)中方法区的区别,其中最主要的区别是8中将方法区转移到本地内存中,且常量池分为运行时常量池和字符串常量池;且字符串常量池被留在内存中的堆中。


image.png

方法区内存溢出

  • jdk1.6 永久代
jdk1.6永久代溢出
  • jdk1.8 元空间


    jdk1.8元空间溢出

场景

  • spring(cglib动态生成代理类,可能造成方法区内存溢出)
  • mybatis(同上)

运行时常量池

常量池:

  • 字符串常量池(string pool)
    字符串常量池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的)。string pool在每个HotSpot VM的实例只有一份,被所有的类共享。在jdk1.8后,将String常量池放到了堆中。
  • class常量池
    当java文件被编译成class文件之后,会在class文件中生成我们所说的class常量池,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(文本字符串、被声明为final的常量、基本数据类型的值)和符号引用(类和接口的全限定名、字段的名称和描述符、方法的名称和描述符)。

运行时常量池:

  1. jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。
  2. 当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。
  3. class常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

总结:

  1. 全局字符串池每个虚拟机只有一个,存储字符串常量的引用值;
  2. class常量池是java程序编译之后才有的,每个类都有,存放字面值和符号引用常量;
  3. 运行时常量池是在类加载完之后,常量池内容存储在运行时常量池中,每个类都有一个,且常量池中符号引用转换为直接引用,与全局字符串池中保持一致。

串池StringTable

  1. 编译期优化
public class Demo3 {
    public static void main(String[] args) {
        String s1 = "a";//a的索引放入串池
        String s2 = "b";//b的索引放入串池
        /*
            new StringBuilder().append("a").append("b").toString();
             s1 s2 为变量,其值没有确定
         */
        String s4 = s1 + s2;//s4放入堆中,因为是变量相加,后面可能会变,并没有将ab索引放入串池
        System.out.println(s4 == "ab");//false
        String s5 = "a" + "b";//直接去寻找串池中是否有ab的索引,有直接返回,没有就将ab索引放入串池
        System.out.println("ab" == s5);//true
    }
}
  1. 字符串延迟加载(惰性)
        //运行到才会将字符串写入串池,不提前放
        String s1 = "a";
        //java.lang.string.count = 2361
        String s2 = "b";
        //java.lang.string.count = 2362
        String s3 = "ab";
        //java.lang.string.count = 2363
  1. 常量池和串池的关系
  • 常量池中的都会被加载到运行时常量池中,此时字符串都是常量池中的符号,尚且未转化为对象
  • 运行时,将String字面量符号“a”转化为字符串对象,并在StringTable中寻找“a”,如多没有则添加“a”字符串对象。
  • StringTable 结构为HashTable,且不能扩容。
  • String table中存储的并不是String类型的对象,存储的而是指向String对象的索引,真实对象还是存储在堆中。
  1. intern方法的运用(jdk1.8)
public class Demo4 {
    public static void main(String[] args) {
        //new String("a") new String("b") new String("ab")
        //StringTable["a","b","ab"]
        String s = new String("a") + new String("b");

        //使用intern方法 在StringTable中放入此字符串对象,如果有则返回给字符串对象,没有会把s对象放入串池
        String s2 = s.intern();
        //s2指向的是串池中字符串对象 s未被放入串池,还在堆中
        String s3 = "ab";
        System.out.println(s3==s2);//true
        System.out.println(s3==s);//true
    }
}
  1. intern方法的运用jdk1.6
public class Demo4 {
    public static void main(String[] args) {
        //new String("a") new String("b") new String("ab")
        //StringTable["a","b"]
        String s = new String("a") + new String("b");
        //使用intern方法 在StringTable中放入此字符串对象,如果有则返回给字符串对象,没有则++复制一份后放入++
        String s2 = s.intern();
        //s2指向的是串池中字符串对象(s复制后的对象) ,s未被放入串池,还在堆中
        //StringTable["a","b","ab"]
        String s3 = "ab";
        System.out.println(s3==s2);//true
        System.out.println(s3==s);//false
    }
}
  1. 测试
public class Demo2 {
    public static void main(String[] args) {
        //StringTable:"a"
        String s1 = "a";
        //StringTable:"a","b"
        String s2 = "b";
        //StringTable:"a","b","ab"
        String s3 = "a" + "b";
        //StringTable:"a","b","ab"
        //s4在堆中
        String s4 = s1 + s2;
        //StringTable:"a","b","ab"
        //此时串池已有了,直接返回
        String s5 = "ab";
        //StringTable:"a","b","ab"
        //此时串池已有了,直接返回
        String s6 = s4.intern();

        System.out.println(s3 == s4);//false
        System.out.println(s3 == s5);//true
        System.out.println(s3 == s6);//true;

        ////StringTable:"a","b","ab","c","d"
        String x2 = new String("c") + new String("d");
        //StringTable:"a","b","ab","c","d","cd"
        //发现串池中没有cd索引,则将cd索引放入串池,
        // jdk1.8则直接将x2对象指向串池cd索引,
        // jdk1.6会复制一份对象来指向串池中的cd索引
        x2.intern();
        String x1 = "cd";
        System.out.println(x1 == x2);//jdk1.8:true,jdk1.6:false
    }
}
  1. StringTable位置
  • jdk1.6及之前,StringTable与方法区一同在永久代中。
  • jdk1.8之后,方法区转移到本地内存中,但是将StringTable转移到堆内存中。
    原因:
    1). StringTable中存在大量的字符串对象,运行时间增长永久代内存占用过多,且永久代只有在触发FULL GC时才进行垃圾回收,回收频率过慢。
    2). 转移到堆中可以利用虚拟机在堆内存中频繁的垃圾回收,处理StringTable中对象过多情况。
  1. StringTable垃圾回收
//-Xmx10m  -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
public class stringTableGc {
    public static void main(String[] args) throws  InterruptedException{
        int i = 0;
        try {
            for (int j = 0;j< 10000; j++){
                String.valueOf(j).intern();//不停的加入到串池
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally {
            System.out.println();
        }
    }
}

通过限定jvm内存大小和打印串池统计,我们发现,GC (Allocation Failure),由于内存不足触发了垃圾回收机制,回收了串池中的部分字符串。

"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc "-javaagent:D:\IntelliJ IDEA 2019.3\lib\idea_rt.jar=8211:D:\IntelliJ IDEA 2019.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\IdeaProjects\test\target\classes;C:\Users\love\.m2\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;C:\Users\love\.m2\repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;C:\Users\love\.m2\repository\org\ow2\asm\asm\7.1\asm-7.1.jar;C:\Users\love\.m2\repository\org\apache\rocketmq\rocketmq-client\4.7.0\rocketmq-client-4.7.0.jar;C:\Users\love\.m2\repository\org\apache\rocketmq\rocketmq-common\4.7.0\rocketmq-common-4.7.0.jar;C:\Users\love\.m2\repository\org\apache\rocketmq\rocketmq-remoting\4.7.0\rocketmq-remoting-4.7.0.jar;C:\Users\love\.m2\repository\com\alibaba\fastjson\1.2.62\fastjson-1.2.62.jar;C:\Users\love\.m2\repository\io\netty\netty-all\4.0.42.Final\netty-all-4.0.42.Final.jar;C:\Users\love\.m2\repository\org\apache\rocketmq\rocketmq-logging\4.7.0\rocketmq-logging-4.7.0.jar;C:\Users\love\.m2\repository\io\netty\netty-tcnative-boringssl-static\1.1.33.Fork26\netty-tcnative-boringssl-static-1.1.33.Fork26.jar;C:\Users\love\.m2\repository\commons-validator\commons-validator\1.6\commons-validator-1.6.jar;C:\Users\love\.m2\repository\commons-beanutils\commons-beanutils\1.9.2\commons-beanutils-1.9.2.jar;C:\Users\love\.m2\repository\commons-digester\commons-digester\1.8.1\commons-digester-1.8.1.jar;C:\Users\love\.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;C:\Users\love\.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;C:\Users\love\.m2\repository\org\apache\commons\commons-lang3\3.4\commons-lang3-3.4.jar" jvm.stringTableGc
[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->776K(9728K), 0.0017733 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

Heap
 PSYoungGen      total 2560K, used 1197K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 33% used [0x00000000ffd00000,0x00000000ffdad5a0,0x00000000fff00000)
  from space 512K, 98% used [0x00000000fff00000,0x00000000fff7e030,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 7168K, used 272K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 3% used [0x00000000ff600000,0x00000000ff644000,0x00000000ffd00000)
 Metaspace       used 3444K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     14138 =    339312 bytes, avg  24.000
Number of literals      :     14138 =    601448 bytes, avg  42.541
Total footprint         :           =   1100848 bytes
Average bucket size     :     0.707
Variance of bucket size :     0.711
Std. dev. of bucket size:     0.843
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000 #桶个数
Number of entries       :     11694 =    280656 bytes, avg  24.000  #实例个数
Number of literals      :     11694 =    635080 bytes, avg  54.308
Total footprint         :           =   1395840 bytes
Average bucket size     :     0.195
Variance of bucket size :     0.209
Std. dev. of bucket size:     0.457
Maximum bucket size     :         3

9.StringTable调优

  • 调整hash表中桶子个数,-XX:StringTableSize=桶个数(桶个数越大性能越好)
  • 考虑字符串是否入池(如果含有大量重复字符串,由于串池不会重复,可以去重引用,节省堆内存的占用)

2.6 直接内存

  • 常见于NIO操作中,用于数据缓冲
    NIO:面向块的方式处理数据(数据块的移动,一次操作产生或者消费一个数据块,将最耗时的 I/O 操作–填充和提取缓冲区内容操作转移回操作系统)
  • 分配回收成本高,但读写能力强
  • 不受JVM内存回收管理

直接内存使用前和使用后

  1. 使用前
  • 因为java无法操作本地文件,所以需要在java堆内存中划出java缓冲区;
  • 从用户态转移到内核态,本地方法在系统内存中划出一段系统缓冲区,将磁盘文件分部分缓冲到系统缓冲区中,系统缓冲区在将数据复制到java缓冲区中;
  • 内核态转到用户态,调用输出流写入操作,将文件copy到另一个位置,循环copy,直到全部复制完成。
image.png
  1. 使用后
  • ByteBuffer.allocateDirect(_size),在系统内存中分配直接内存;
  • 系统方法和java方法都可以访问直接内存;
  • 与不使用直接内存相比,减少了一次从系统缓存区向java缓冲区复制的操作,复制效率成倍上升。
image.png

直接内存溢出

public class Demo3 {

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while(true){
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100*1024*1024);
                list.add(byteBuffer);
                i++;
            }
        }finally {
            System.out.println(i);
        }
    }
}

36  3.6G
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at com.example.jvm.Demo3.main(Demo3.java:14)

分配和回收原理

  • 使用Unsafe对象实现直接内存的分配回收,回收主要使用的是freeMemory方法,**而不是通过GC回收的。 **
  • ByteBuffer类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦对象被回收,就会由ReferenceHandler线程通过Cleaner的clean对象调用fUnsafe的reeMenory来释放直接内存。
  • -XX:+DisableExplicitGC 显式的System.gc()显式的垃圾回收 FULL GC,被禁用。
  • 因为考虑到系统性能,FULL GC时间够长,会严重影响性能。所以涉及到直接内存的使用,释放内存使用Unsafe.freeMemory,不建议使用System.gc()。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • java目前使用的是hot spot jvm引擎自带JIT编译器,可以将频繁执行的方法侦测为热点代码,增加执行速度...
    大力水手_阅读 607评论 2 1
  • jvm内存结构 1.程序计数器 1.1 定义 Program Counter Register 程序计数器(寄存器...
    韩who阅读 381评论 0 0
  • Java和C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进来,墙里面的人想出来。 对象...
    胡二囧阅读 1,089评论 0 4
  • 前言 Java是一种跨平台的语言,当初其设计初衷也是为了解决各个平台编译环境具有差异,对程序移植性问题造成困难这一...
    ObjectSpace阅读 308评论 0 1
  • JVM有哪些分区?程序计数器,java虚拟机栈,本地方法栈,堆,方法区。 java栈中存放的是一个个栈帧,每一个栈...
    irckwk1阅读 695评论 0 0