首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

JVM是否会阻止尾调用优化?

JVM(Java虚拟机)在某种程度上会阻止尾调用优化,因为在Java中,尾调用优化的实现需要虚拟机确定当前栈帧是最后一个栈帧,而这个问题在JVM实现时并不是一个简单的操作。尾调用优化主要是用来提高程序运行速度和减少内存浪费。

在JVM的规范中,规定了Java中的尾调用必须满足一些条件才能进行优化。其中之一就是当前栈帧的调用方法必须是最后一个方法调用。这意味着在调用方法之前,必须在当前栈帧中执行完所有其他的方法。如果有一个方法在栈帧的底部调用,那么这种方法就不能被视为尾调用,因此JVM无法进行优化。

除了满足最后一个方法调用之外,尾调用优化的实现还需要确定当前栈帧是最底部栈帧。这意味着JVM必须能够确定方法调用堆栈中存在的所有栈帧,并且确定每个栈帧的调用关系和它们的类型。这些操作在运行时是非常复杂的,需要使用一些高级的数据结构和算法来完成。然而,由于JVM的一些限制和实现复杂性,尾调用优化的实现并不是万能的。因此,JVM可能会阻止尾调用优化,或者优化效果并不是很明显。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

调用优化

二、调用优化 调用之所以与其他调用不同,就在于它的特殊的调用位置。 我们知道,函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。...这就叫做"调用优化"(Tail call optimization),即只保留内层函数的调用记录。如果所有函数都是调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。...这就是"调用优化"的意义。 三、递归 函数调用自身,称为递归。如果调用自身,就称为递归。...对于其他支持"调用优化"的语言(比如Lua,ES6),只需要知道循环可以用递归代替,而一旦使用递归,就最好使用递归。...arguments:返回调用时函数的参数。 func.caller:返回调用当前函数的那个函数。 调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。

71550

如何优化调用

需要了解如何优化递归的话,我们需要从最开始讲起。 什么是调用 什么是递归 如何优化递归 调用 从字面理解,自然而言就是在函数的尾部返回一个函数的调用,通常来说,指的是函数执行的最后一步。...如果递归链过长,可能stack overflow 那么我们是不是可以做优化呢,这就可以涉及上面提到的调用,它的原理是啥呢?...因为调用时函数的最后一部操作,所以不再需要保留外层的调用帧,而是直接取代外层的调用帧,所以可以起到一个优化的作用。...手动优化 既然我们知道了,很多浏览器对于递归的优化支持的浏览器并不多,那你会好奇,当我们使用递归进行优化的时候,依然出现栈溢出的错误,那么我们如何解决呢??...对于递归而言,我们需要了解优化它的原理,如果有必要的话,将递归的形式写成迭代的形式,通过迭代方式,降低重复值的计算,当然了,这个过程,有时候是比较难的,值得我们去思考。 参考 调用递归

85430

递归调用优化

之前分享过递归,其中有一个优化就是调用。 先明确调用的概念: 调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是return调用另一个函数。...注意,并不是所有的函数都能调用优化,要看你这个函数需不需要使用某些上个函数的变量或者什么的。...调用优化其实很大一部分就是递归函数在使用,因为递归函数调用的时候非常耗费内存,可能需要保存成百上千调用栈,很容易内存溢出。如果是递归就只有一个调用栈,能把复杂度O(n)的变成O(1)。...Function) { f = f(); } return f; } 执行: trampoline(sum(1, 100000)) 你会发现,很多递归函数都能改成类似的,然后使用蹦床函数实现调用优化...而ES6对调用有什么优化?就是函数默认值,在一些场景下,比如阶乘的递归,采用默认值实现递归优化。 (完)

66510

图解调用优化

Photo by Benni Asal on Unsplash 调用 啥是调用调用就是函数的最后一个步骤调用另一个函数 比方说: ?...20190307171547.png 函数在调用的时候会在调用栈中 push 一个调用帧,每次执行完函数都会逐一弹出调用帧知道所有函数执行完毕,调用栈被清空: 调用栈中的同步代码 1function f1...首先执行 script ,将 main 主程序推入调用栈中并执行,发现需要调用 f3 将 f3 函数推入调用栈中,执行 f3,发现需要调用 f2 将 f2 函数推入调用栈中,执行 f2, 发现需要调用...最后将 console.log 弹出调用栈,代码执行完毕 调用优化 每次在函数被调用的时候,内存都会保存调用帧。...调用因为是函数的最后一步,因此并不需要外层函数的调用帧。我们只需要将最后需要执行另外一个函数之前用 return 操作符显式表明"不再需要此函数"即可 ?

44210

JavaScript 中的调用优化

递归 顾名思义,在一个调用中,如果函数最后的调用位置上是这个函数本身,则被称为递归。递归很常用,但如果没写好的话也非常消耗内存,导致爆栈。...注意很多介绍调用递归的文章讲到这里就结束了,实际上情况并非这么简单,调用在没有进行任何优化的时候和其他的递归方式一样,该产生的调用栈一样产生,一样会有爆栈的危险。...首先通过闭包,在 tailCallOptimize 的作用域中保存唯一的 active 和 accumulated,其中 active 指示递归优化过程是否开始,accumulated 用来存放每次递归调用的参数...单独的函数调用不是调用 下面这个函数是否包含调用呢: function foo() {  bar()} 答案是否定的,还是先将函数改写一下: function foo() {  bar()return...堆栈信息丢失 除了开发者难以辨别调用以外,另一个原因则是堆栈信息会在优化的过程中丢失,这对于调试是不方便的,另外一些依赖于堆栈错误信息来进行用户信息收集分析的工具可能失效。

1K10

JS 调用栈机制与 ES6 调用优化介绍

针对这种情况除了我们要尽量避免函数层级嵌套的比较深之外,ES6提供了“调用优化”来解决调用侦过多,引起的内存消耗过大的问题。 何谓调用调用指的是:函数的最后一步是调用另一个函数。...// return undefined; // 隐式的return } 调用优化优化了什么?...} 调用优化只在严格模式下开启,非严格模式是无效的。...如果环境不支持“调用优化”,代码还可以正常运行,是无害的!...更多: 关于递归以及更多调用优化的内容,推荐查阅ES6入门-阮一峰 调用栈debug大法 查看调用栈有什么用 查看函数的调用顺序是否跟预期一致,比如不同判断调用不同函数。

86220

js 调用栈机制与ES6调用优化介绍

针对这种情况除了我们要尽量避免函数层级嵌套的比较深之外,ES6提供了“调用优化”来解决调用侦过多,引起的内存消耗过大的问题。 何谓调用调用指的是:函数的最后一步是调用另一个函数。...// return undefined; // 隐式的return } 调用优化优化了什么?...} 调用优化只在严格模式下开启,非严格模式是无效的。...如果环境不支持“调用优化”,代码还可以正常运行,是无害的!...更多: 关于递归以及更多调用优化的内容,推荐查阅ES6入门-阮一峰 调用栈debug大法 查看调用栈有什么用 查看函数的调用顺序是否跟预期一致,比如不同判断调用不同函数。

66820

来来来,我们聊一聊,为什么不建议使用递归操作?

但我们在听到这句话的时候,是否产生过疑问,为什么不建议使用递归操作呢? 现在,我们就一起聊聊这个话题,看看递归到底产生什么样的问题。 首先,我们思考一道算法题:如何实现二叉树的中序遍历?...接下来,我们再从 JVM 的层面上,分析递归可能产生的问题。...在 JVM 中,方法调用的过程大致为: 除非被调用的方法是类方法,否则在每一次方法调用指令之前,JVM 先把方法被调用的对象引用压入操作数栈中,除了对象的引用之外,JVM 还会把方法的参数依次压入操作数栈...优化的方法 说的这里,我们不妨再来聊聊如何优化递归,其方法主要有三个,分别为: 限制递归次数 借助堆栈将递归转化为非递归 使用递归形式 限制递归次数 对于“限制递归次数”来说,就是在调用函数的时候,同时传入一个数字...说了这么多,那么递归形式是否真的有优化效果呢?

43920

来来来,我们聊一聊,为什么不建议使用递归操作?

但我们在听到这句话的时候,是否产生过疑问,为什么不建议使用递归操作呢? 现在,我们就一起聊聊这个话题,看看递归到底产生什么样的问题。 首先,我们思考一道算法题:如何实现二叉树的中序遍历?...接下来,我们再从 JVM 的层面上,分析递归可能产生的问题。...在 JVM 中,方法调用的过程大致为: 除非被调用的方法是类方法,否则在每一次方法调用指令之前,JVM 先把方法被调用的对象引用压入操作数栈中,除了对象的引用之外,JVM 还会把方法的参数依次压入操作数栈...优化的方法 说的这里,我们不妨再来聊聊如何优化递归,其方法主要有三个,分别为: 限制递归次数 借助堆栈将递归转化为非递归 使用递归形式 限制递归次数 对于“限制递归次数”来说,就是在调用函数的时候,同时传入一个数字...说了这么多,那么递归形式是否真的有优化效果呢?

83200

大家都知道递归,递归呢?什么又是递归优化

从“”字可看出来即若函数在尾巴的地方递归调用自己。...原因就是因为编译器帮助做了递归优化,可以打开汇编代码看看(这里就不展示 C++的了)。后面我用大家比较熟悉的 JVM based 语言 Scala 来阐述这个优化过程。...(好像 Java 的编译器没做这方面的优化,至少我实验我本地 JDK8 是没有的,不清楚最新版本的有木有)(scala 本身提供了一个注解帮助编译器强制校验是否能够进行尾递归优化@tailrec) object...默认启用递归优化正常计算结果,禁用递归优化则“StackOverflow”。 我们来看看生成的字节码有什么不同。 ? 包含递归优化的字节码,直接 goto 循环。 ?...禁用递归优化的字节码,方法调用。 从上面可以看出,递归优化后,变成循环了(前面的 C++ 类似)。 好了,递归咱们就了解到这里。

1.5K30

在Java中谈递归--递归和垃圾回收的比较(转载)

本质还是调用一个方法,只是这个方法正好是自身而已 递归因为是在自身中调用自身,所以带来以下三个显著特点: 调用的是同一个方法 因为1,所以只需要写一个方法,就可以让你轻松调用无数次(不用一个个写,你定个...一个误区,不是因为调用自身而开销巨大,而是嵌套加上轻易就能无数次调用,使得递归可以很容易开销巨大 既然导致内存溢出泄露如此,那肯定要想办法了,方法很简单,那就是递归优化 二、递归优化 递归优化是利用上面的第一个特点...“调用同一个方法”来进行优化递归优化其实包括两个东西:1)递归的形式;2)编译器对递归的优化 递归的形式 递归其实只是一种对递归的特殊写法,这种写法原本并不会带来跟递归不一样的影响,它只是写法不一样而已...】,这种说法可能导致误解,因为不是只告诉编译器就行,而是你需要做优化的前半部分,之后编译器做后半部分 所以总结:为了解决递归的开销大问题,使用递归优化,具体分两步:1)你把递归调用的形式写成递归的形式...下面虽然是在说JAVA,但是C也是差不多的 在Java中, JVM中的栈记录了线程的方法调用。每个线程拥有一个栈。

1.3K50

深入理解jvm - 编译优化(下)

前言 本文接上文的内容继续讲述:深入理解jvm - 编译优化(上) 概述 补充后端优化的另一项内容提前编译器的处理 介绍jvm的几项重点优化措施 「方法内联(重要)」 「逃逸分析(先进)」 「公共子表达式消除...底层优化 下面是关于jvm的底层优化内容,jvm的底层优化内容非常多,比如:方法内联、冗余重复消除、复写传播、无用代码消除等等。...,例如作为调用参数传递到其他方法中。...的底层在每次的操作的时候都需要对于数组的边界进行检查操作,即一个含头不含的判断:[start, end)....**但是hotspot根据实际的方式进行动态判断选择使用边界检查消除还是使用原始的策略模式运行。

66810

Java并发机制的底层实现原理之volatile应用,初学者误看!

volatile的介绍:   Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM...相反,它会锁定这块内存区域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制阻止同时修改由两个以上处理器缓存的内存区域数据。...2.volatile的使用优化   著名的Java并发编程大师Doug lea在JDK 7的并发包里新增一个队列集合类LinkedTransferQueue,它在使用volatile变量时,用一种追加字节的方式来优化队列出队和入队的性能...64字节的话,处理器会将它们都读到同一个高速缓存行中,在多处理器下每个处理器都会缓存同样的头、节点,当一个处理器试图修改头节点时,会将整个缓存行锁定,那么在缓存一致性机制的作用下,导致其他处理器不能访问自己高速缓存中的节点...Doug lea使用追加到64字节的方式来填满高速缓冲区的缓存行,避免头节点和节点加载到同一个缓存行,使头、节点在修改时不会互相锁定。

60320

暴力破解美团最新JVM面试题

2、为什么catch Exception抛出? 怎么查看栈深度呢?JVM提供了相关方法吗?木有!我们通过Linux命令来统计 [348fuye26z.png?...2、JVM内部做了优化,比如栈帧回溯、递归内联、递归优化。验证这个的时候还得考虑方法执行的两个阶段:解释执行,执行JIT及时编译后的代码。是真滴复杂!...JVM的虚拟机栈是在操作系统的线程栈上进行拓展的,Linux系统的默认线程栈是8M,如果是递归调用,一下就跑完了。...首先,栈的内存大小决定了,一个程序的调用深度是有限的,超过了栈内存大小,Linux触发段错误信号:SIGSEGV。JVM应该是捕获了这个信号,并进行了处理。那什么样的处理能支持程序一直运行下去呢?...最终探索 针对回调做优化,目前主流的优化方式有:递归优化、内联优化JVM没有用递归优化,而是用的内联优化,专业名词叫递归内联。 此结论来自之前看的R大的某篇文章。本来想找到贴出来的,没找着。

40060

Java并发编程的艺术

64字节的话,处理器会将它们都读到同一个高速缓存行中,在多处理器下每个处理器都会缓存同样的头、节点,当一个处理器试图修改头节点时,会将整个缓存行锁定,那么在缓存一致性机制的作用下,导致其他处理器不能访问自己高速缓存中的节点...2.轻量级锁 偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁; (1)轻量级锁加锁 线程在执行同步块之前,JVM先在当前线程的栈桢中创建用于存储锁记录的空间...特别记录一下 当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带PermGen(jdk1.8则是metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,锁所有调用该方法的线程...如果物理机器有多个处理器,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁。...如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM消除它身上的偏向锁,将锁恢复到标准的轻量级锁。 它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。

69820

Java的堆内存和栈内存,内存泄露和溢出问题的排查与处理

当一个方法调用结束时,该方法的栈帧会被销毁,栈内存自动释放该方法使用的空间。区别和联系:区别:堆内存用于存储对象实例和数组,而栈内存用于存储方法调用和局部变量。...区别:堆内存的分配和释放由 JVM 自动进行,而栈内存的分配和释放由方法的调用和结束进行自动管理。联系:堆和栈都是内存中的存储区域,用于支持 Java 的运行和内存管理。...联系:堆内存和栈内存都是 JVM 在运行时根据程序需要进行分配和管理的,在编写 Java 程序时,可以根据需求合理地使用堆内存和栈内存来优化程序性能和内存占用。...长时间运行的程序,对于未清理的资源,消耗掉可用内存。使用了大量的递归,导致堆栈溢出。使用了大量的循环,导致CPU内存持续增长,最终溢出。...以下是常见的处理方法:检查代码中是否存在资源没有及时释放的情况,例如数据库连接、文件流等,确保在使用完毕后进行关闭操作。尽量避免使用过多的递归调用,可以使用迭代或递归等方式来进行优化

46251

面试专题-并发篇

,同样不占用 cpu 时间 当其它持锁线程调用 notify() 或 notifyAll() 方法,按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁 如果等待超时,也从有时限等待状态恢复为可运行状态...- 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等 handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,触发拒绝策略 抛异常 java.util.concurrent.ThreadPoolExecutor.AbortPolicy...synchronized 的区别 理解 ReentrantLock 的公平、非公平锁 理解 ReentrantLock 中的条件变量 三个层面 不同点 语法层面 synchronized 是关键字,源码在 jvm...起因:由于编译器优化、或缓存优化、或 CPU 指令重排序优化导致指令的实际执行顺序与编写顺序不一致 解决:用 volatile 修饰共享变量会在读、写共享变量时加入不同的屏障,阻止其他读写操作越过屏障...,从而达到阻止重排序的效果 注意: volatile 变量写加的屏障是阻止上方其它写操作越过屏障排到 volatile 变量写之下 volatile 变量读加的屏障是阻止下方其它读操作越过屏障排到 volatile

54610
领券