在去年年初我为了测试一个出题平台,随手打了几道题目,水了一篇Java虚拟机测试题。没想到今天一看竟然有人回答!热泪盈眶啊……好,那既然你肯回答,我就肯改主观题。截止目前,答题人数为24(有一次是我自己测试)。平均正确率是36%,平均答题时间竟然高达42:37!好感动
。那时隔
一年,我们来好好分析下这个测试,并看看这个测试能体现出什么。如果你想先测试下自己的实力,可以点击上面的链接去答题页面。
那么,接着让我们来分析一下题目与答题状况:
截止JDK1.8,以属于Java的并行GC的是:
A.ParNew
B.Serial
C.Serial Old
D.Parallel Scavange
E.G1
第一题考察的内容很基本,就是对几大垃圾收集器的了解。
Serial收集器是最古老的收集器,在JDK1.3.1之前甚至是新生代收集的唯一选择。不过,这是一个单线程且采用复制算法,仅对新生代作用的收集器。由于比较旧,触发GC时就会采用“The World”。
你的线程都停下来了,那可怎么办呢JOJO
所有在运行的东西都会停下来!
Serial Old并不是旧版本的Serial收集器,而是Serial收集器的老年代版本。它使用标记-整理算法,同样是多线程的,也同样具有 厉害的 “The World”技能。
ParNew是Serial收集器的升级版本,也即多线程版本。其他的都一样,该时停的照样得停。
Parallel Scanvenge也是使用了复制算法的,作用于新生代的收集器。较之Serial,它跟追求增加用户代码运行时间的比重,也就是说,不管单次收集时间有多长,它着眼于减少收集时间的比重。而且它还有自动调节参数的功能。
Parallel Old就是适用于老年代的Parallel Scanvenge收集器,和Serial Old类似,它也是使用标记-整理算法的。其他的不同和Serial与Parallel Scanvenge的不同类似。
虽然题干中没提到CMS,但我还是想提及这个收集器。它全称Concurrent Mark Sweep,很明显这是标记-整理算法的收集器。而且,由于并发(Concurrent),CMS的垃圾清理并不需要停止整个程序。但是还CMS还是要时停的……因为初始标记和重新标记两步为了保证一致性,并不能并发的完成。
并发吗……不错的力量和速度。我Serial生活的那个时代,只有单线程可以用啊……
不仅如此,CMS还有很多其他的缺点。
,可惜比较早)
提到移除CMS是一个漫长的过程,所以暂时似乎还能用着CMS过日子?
G1收集器是
全新的,也是最新的收集器,在JDK 7u4之后已经完全开放商用了。它是标记-整理算法与复制算法的一种结合,而这种结合建立在将内存分为一个个Region的基础上。G1能同时作用于新生代和老年代,而且进一步的讲,G1收集器直接作用于整个Java堆!从上面那些邮件中,开发者吹爆G1的态度可以看出,众开发者可是对G1寄予厚望的。不过我看到吐槽G1的声音依然不少(甚至有说“Use G1 when you have tons of memory and don’t care about burning CPU… :wink:
”哈哈哈),可见G1虽然从04年第一篇论文开始到12年商用花了8年之久,它要走的优化之路还很漫长。
这题说难不难,只要看看带Parallel的就能选出A、D,剩下的E看自己对JVM的了解。在答题过程中,很多人漏选了ParNew。
关于以下GC日志的说法,正确的是:
13.112: [GC [DefNew: 3324K->152K(3712K), 0.0023122 secs] 3324K->152K(11904K), 0.0035311 secs]
A.13.112代表自上一条log记录时过了13.112秒
B.JVM当前的GC收集器为Serial
C.3324K->152K(3712K)代表清理前占用内存3324K,清理后占用152K,该区域剩余内存3712K
D.本次操作总用时为0.0023122+0.0035311秒
这题没啥好说的,考验的就是对日志的阅读能力。这里说明下这段日志的读法。最开始的时间是自虚拟机启动后过去的时间。GC是这里垃圾收集的类型,即是为Full GC。随后的是“GC前区域内存->GC后区域内存(区域总内存)”,然后再是GC的执行时间。这两个时间是包含而不是叠加关系。
这里要注意的是,DefNew可能会和ParNew搞混。其实DefNew就是指默认新生代收集器,也就是Serial收集器。所以答案明显应选择B。这题选错的人不多,错项集中在C。
以下关于Java虚拟机的说法错误的是:
A.类对象不能被回收
B.Java堆不一定能扩展
C.一般来说,大对象的回收周期较小对象长
D.方法区溢出的错误提示信息是PermGen Space
这一题的考察点比较杂。
首先是类对象。因为是对象,Class对象就存在于Java堆中……对吗?答案是否定的,Class对象并没有明确规定存在于Java堆。而事实上,Hotspot中,Class对象存放在方法区中。那么Class对象是不是就不会被回收了呢?也不是,JDK8之前方法区的实现是永久代,所以虽然条件苛刻,但是Class对象也是可以被回收的。而JDK8之后永久代被废弃,并引入了元空间。虽然元空间并不会单独回收一个类,不过,当元空间的大小达到MaxMetaspaceSize时,依旧会触发回收。嘛,不管怎样,类对象都是可以被回收的。
只要设置-Xms于-Xmx一样Java堆就不会自动扩展了。
大对象的回收周期理论上就应该更长,因为回收大对象比回收小对象更加耗时。在JVM中,大对象会直接进入老年代,而老年代的回收周期较新生代要短的很多。
方法区在JDK8之前是以永久代的形式存在的,所以那时溢出的错误提示信息是PermGen Space。而JDK8开始引入元空间之后,错误提示信息也就随之变为Metaspace了。
正确答案无疑是A,不过由于JDK8开始移除了永久代,选择D也是有道理的。这题每个选项都有人选,我比较懵逼。
以下为GC Root的对象是
A.gc链链顶对象
B.JNI方法的local对象
C.方法的基本类型局部变量
D.类静态字段的对象
E.所有线程
第四题都没啥好说的了。A选项已经明显的不能在明显了,而所有人也基本都选择了这项。和local有关的基本都是GC Root。C选项甚至都不是对象!最后E选项可能会错选,注意,只有活着的线程才能作为GC Root。所以答案就是A、B、D,没有什么特别大的问题。
以下是一段java代码:
StringBuilder sBuilder = new StringBuilder("ja"); String java = sBuilder.append("va").toString(); System.out.println(java.intern() == java); String drink = sBuilder.append("hothothothot").toString(); System.out.println(drink.intern() == drink);
资料: Java SE 7 Features and Enhancements 。通过此段代码与资料,请以JDK 6、JDK 7为例简单阐述JDK 7更新的特性。(提示:请分别给出两版本的运行结果,指出带来此变化的更新并分析)
这是一道考察同时考察文档阅读与JVM知识的题目,曾经被我当作入群题
)”。其次就是对String::intern方法的理解了。首先要注意的是,如果字符串由toString方法创建,那么字符串会出现在Java堆中。
在JDK 6中,intern方法会将第一次出现的字符串复制进永久代,然后返回其引用。而JDK 7中intern方法不再复制,而是改为记录改字符串的引用。也就是说,对于新的字符串,JDK 6的intern会再复制进常量池,然后返回其 在常量池的引用; 而JDK 7的intern会把原字符串的引用复制进常量池,并依旧返回 原字符串的引用 。不过这里比较坑的是,在编译期字符串“java”就已经被加入到常量池中了,所以intern会直接返回其在常量池中的引用。综上,正确的答案是:
在JDK 6中:
false
false
在JDK 7中:
false
true
答对的人还是有的,不过我注意到还有一位把JDK 6和JDK 7的情况写反了,只差一点点啊23333
没想到随手一出的题还有那么多dalao愿意回答,真的很感动。而由于没有指定环境,还出现了第三题那样的双选,真是很不严谨。而且熟悉的dalao应该能发现,这基本就是《深入理解Java虚拟机》这本书的练习题!嘛,还是感谢一下24位dalao的捧场。其中,有三位dalao是全对的:
看来这种活动以后可以多多搞搞23333