作者个人研发在高并发场景下提供了一个简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。开源半年多以来,已成功为十几家中小企业提供精准定时调度解决方案,经受住了生产环境的考验。为了造福更多的童鞋,这里给出开源框架的地址:https://github.com/sunshinelyz/mykit-delay我之前写的。我们刚开始学习Java的时候接触到这样一个观点:Java中的对象是在堆上创建的,而对象的引用是放在栈上的。这个观点真的正确吗?如果是正确的,为什么面试官会问:“Java中的对象一定是在堆上分配的吗?”这个问题呢?看来我们从接触Java中被灌输的观点是值得我们怀疑的。关于面试题题目中的面试题是:Java中的对象和数组是在堆上分配的吗?当面试官问到这个的时候,有的朋友会在心里想:我初学Java的时候就知道:Java中的对象是在堆上创建的,对象引用是存放在栈上的。那么Java中的对象和数组必须分配在堆上!不是吗?如果你这样回答,你将直接被PassedLose。可能有些小伙伴还不太明白,下面我们继续往下看。面试题答案首先我们先给出这个问题的答案。在这里我简单回答一下这个面试题,后面我们会进行相关分析。你可以这样回答:Java中的对象不一定分配在堆上,因为JVM可以通过逃逸分析分析出一个新对象的使用范围,并以此来决定是否将这个对象分配在堆上。如果JVM发现有些对象没有escapemethod,很可能会优化分配到栈上。在这里,我们接触到了一个新名词:逃逸分析。相信很多小伙伴都不是很了解,下面我们继续往下看。逃逸分析逃逸分析的概念首先用官方的形式描述一下什么是逃逸分析。逃逸分析是:一种静态分析,确定指针的动态范围,可以分析指针在程序中的什么地方可以被访问到。在JVM的即时编译上下文中,逃逸分析将确定新创建的对象是否逃逸。即时编译判断对象是否逃逸:一是对象是否存储在堆中(堆中对象的静态字段或实例字段),二是对象是否被传入未知代码。直接讲这些概念确实有点晕,举两个例子吧。对象逃逸示例典型的对象逃逸是:对象被复制到一个可能被外部使用的成员变量或静态变量中,此时变量逃逸。我们可以用下面的代码来表示这种现象。/***@authorbinghe*@description对象转义实例1*/publicclassObjectEscape{privateUseruser;publicvoidinit(){user=newUser();}}在ObjectEscape类中,有一个成员变量user,我们在init()方法中,User类的一个对象被创建并赋值给成员变量user。这时候把对象复制到成员变量中,可能被外部使用,此时的变量逃逸。另一种典型的场景是:通过return语句返回对象。如果通过return语句返回对象,此时的程序无法判断后面是否会使用到该对象,而外部线程可以访问到这个变量,此时对象也已经逃逸了。我们可以用下面的代码来表示这种现象。/***@authorbinghe*@description对象逃逸例子2*/publicclassObjectReturn{publicUsercreateUser(){Useruser=newUser();returnuser;}}举两个例子,相信小伙伴们对JVM嗯,没错,JVM可以通过逃逸分析来分析新对象的使用范围,从而决定新对象是否应该分配到堆上。还没完,下面我们继续看逃逸分析的优势,让小伙伴们更好的了解逃逸分析。逃逸分析的优点逃逸分析的优点一般可以分为三类:对象可以在栈上分配,分离对象或标量替换,消除同步锁。我们可以用下图来表示。可以在堆栈上分配对象。JVM可以通过逃逸分析分析新对象的使用范围,从而在栈上分配对象。栈分配可以在栈帧上快速创建和销毁对象,无需将对象分配到堆空间,可以有效降低JVM垃圾回收的压力。对象分离或标量替换当JVM通过逃逸分析决定在栈上分配对象时,即时编译可以打散对象并用小的局部变量替换它们。我们称此分解过程为标量替换。把对象替换成局部变量后,就可以很方便的在栈上分配了。同步锁消除如果JVM通过逃逸分析发现一个对象只能从一个线程访问,那么在访问这个对象时就没有必要加同步锁。如果程序中使用了synchronized锁,JVM会消除synchronized锁。这里需要注意的是,这种情况是针对synchronized锁而言的,但是对于Lock锁,JVM是无法消除的。要启用同时消除,您需要添加-XX:+EliminateLocks参数。因为这个参数依赖于逃逸分析,所以-XX:+DoEscapeAnalysis选项也必须打开。因此,并不是所有的对象和数组都分配在堆上。由于即时编译的存在,如果JVM发现有些对象没有escape方法,很可能会优化分配到栈上。本文转载自微信公众号“冰河科技”,可通过以下二维码关注。转载本文请联系冰川科技公众号。
