私有函数使用的计算机资源比 public 多还是少?
Do private functions use more or less computer resources than public ones?
计算机资源是内存、电源、磁盘space。我只是好奇,即使它或多或少只是一点点。
Do private functions use more or less computer resources than public ones?
没有。 JVM 使用相同的资源,而不管各个字段或方法上的访问修饰符如何。
但是,除了资源利用率之外,还有一个更好的理由更喜欢private
(或protected
);即 encapsulation. Also, I highly recommend you read The Developer Insight Series: Part 1 - Write Dumb Code.
理论上,在某些情况下,它可能会快 头发。实际上,它们同样快。
非静态、非public 方法使用invokevirtual
字节码操作调用。此操作码要求 JVM 动态查找实际的方法解析:如果您有一个静态编译为 AbstractList::contains
的调用,它应该解析为 ArrayList::contains
还是 LinkedList::contains
等?更重要的是,编译器不能只在下一次重用这次编译的结果;如果下次 myList.contains(val)
被调用时,它是在不同的实现上怎么办?因此,对于非私有方法,编译器必须至少 一些 量的检查,大致是每次调用。
无法覆盖私有方法,它们是使用 invokespecial
调用的。此操作码用于各种类型的方法调用,您可以只解析一次,然后永远不会更改:构造函数、调用超级方法等。例如,如果我在 ArrayList::add
中,我调用 super.add(value)
(那里没有发生,但让我们假装它发生了),那么编译器可以肯定地知道这是指 AbstractList::add
,因为 class 的超级 class 可以永远不会改变。
所以,粗略地说,invokevirtual
调用需要解析方法然后调用它,而 invokespecial
调用不需要解析方法(在第一次调用之后-- 你必须至少解决所有问题一次!)。
Resolution of the symbolic reference of one occurrence of an invokedynamic instruction does not imply that the same symbolic reference is considered resolved for any other invokedynamic instruction.
For all other instructions above, resolution of the symbolic reference of one occurrence of an instruction does imply that the same symbolic reference is considered resolved for any other non-invokedynamic instruction.
(强调原文)
好的,现在是 "but you won't notice the difference" 部分。 JVM 针对虚拟调用进行了大量优化。它可以做一些事情,比如检测某个站点总是看到一个 ArrayList
,因此 "staticify" List::add
调用实际上是 ArrayList::add
。为此,它需要验证传入的对象是否确实是预期的 ArrayList
,但这非常便宜;如果一些早期的方法调用已经在此方法中完成了该工作,则不需要再次发生。这称为单态调用站点:即使代码在技术上是多态的,但实际上列表只有一种形式。
JVM 优化了单态调用点,甚至是双态调用点(例如,列表始终是 ArrayList
或 LinkedList
,绝不会是其他任何内容)。一旦它看到三种形式,它就必须使用更慢的完全多态分派。但话又说回来,此时你正在将苹果与橙子进行比较:非私有、多态调用对私有调用的定义根据定义。比较两种单态调用(虚拟调用和私有调用)更公平,在这种情况下,您可能会发现差异很小,即使它是可检测的。
我只是做了 a quick JMH benchmark 来比较 (a) 直接访问字段,(b) 通过 public getter 访问它和 (c) 通过私有 getter。这三个人花了同样多的时间。当然,超级微型基准测试很难做到正确,因为 JIT 可以通过优化来完成如此美妙的事情。再一次,这就是 点 :JIT 通过优化做了如此美妙的事情,public 和私有方法一样快。
I am just curious, even though it is more or less by a tiny itty-bitty amount.
虽然好奇是件好事......如果你在编程时开始考虑到这种事情,那么:
您可能会浪费大量时间寻找不需要的微优化,
你的代码很容易无法维护,因为你牺牲了良好的设计原则,
你甚至冒着让你的代码更少效率*的风险。
* - 它可以这样进行。 1) 您花费大量时间调整代码以在测试平台上快速 运行。 2) 当你在生产平台上运行,你发现硬件赋予你不同的性能特征。 3) 您升级 Java 安装,新的 JVM 的 JIT 编译器以不同方式优化您的代码,或者它有一堆新的优化被您的调整 抑制 。 4) 当您 运行 在真实世界的工作负载上编写代码时,您会发现作为调整基础的假设是无效的。
计算机资源是内存、电源、磁盘space。我只是好奇,即使它或多或少只是一点点。
Do private functions use more or less computer resources than public ones?
没有。 JVM 使用相同的资源,而不管各个字段或方法上的访问修饰符如何。
但是,除了资源利用率之外,还有一个更好的理由更喜欢private
(或protected
);即 encapsulation. Also, I highly recommend you read The Developer Insight Series: Part 1 - Write Dumb Code.
理论上,在某些情况下,它可能会快 头发。实际上,它们同样快。
非静态、非public 方法使用invokevirtual
字节码操作调用。此操作码要求 JVM 动态查找实际的方法解析:如果您有一个静态编译为 AbstractList::contains
的调用,它应该解析为 ArrayList::contains
还是 LinkedList::contains
等?更重要的是,编译器不能只在下一次重用这次编译的结果;如果下次 myList.contains(val)
被调用时,它是在不同的实现上怎么办?因此,对于非私有方法,编译器必须至少 一些 量的检查,大致是每次调用。
无法覆盖私有方法,它们是使用 invokespecial
调用的。此操作码用于各种类型的方法调用,您可以只解析一次,然后永远不会更改:构造函数、调用超级方法等。例如,如果我在 ArrayList::add
中,我调用 super.add(value)
(那里没有发生,但让我们假装它发生了),那么编译器可以肯定地知道这是指 AbstractList::add
,因为 class 的超级 class 可以永远不会改变。
所以,粗略地说,invokevirtual
调用需要解析方法然后调用它,而 invokespecial
调用不需要解析方法(在第一次调用之后-- 你必须至少解决所有问题一次!)。
Resolution of the symbolic reference of one occurrence of an invokedynamic instruction does not imply that the same symbolic reference is considered resolved for any other invokedynamic instruction.
For all other instructions above, resolution of the symbolic reference of one occurrence of an instruction does imply that the same symbolic reference is considered resolved for any other non-invokedynamic instruction.
(强调原文)
好的,现在是 "but you won't notice the difference" 部分。 JVM 针对虚拟调用进行了大量优化。它可以做一些事情,比如检测某个站点总是看到一个 ArrayList
,因此 "staticify" List::add
调用实际上是 ArrayList::add
。为此,它需要验证传入的对象是否确实是预期的 ArrayList
,但这非常便宜;如果一些早期的方法调用已经在此方法中完成了该工作,则不需要再次发生。这称为单态调用站点:即使代码在技术上是多态的,但实际上列表只有一种形式。
JVM 优化了单态调用点,甚至是双态调用点(例如,列表始终是 ArrayList
或 LinkedList
,绝不会是其他任何内容)。一旦它看到三种形式,它就必须使用更慢的完全多态分派。但话又说回来,此时你正在将苹果与橙子进行比较:非私有、多态调用对私有调用的定义根据定义。比较两种单态调用(虚拟调用和私有调用)更公平,在这种情况下,您可能会发现差异很小,即使它是可检测的。
我只是做了 a quick JMH benchmark 来比较 (a) 直接访问字段,(b) 通过 public getter 访问它和 (c) 通过私有 getter。这三个人花了同样多的时间。当然,超级微型基准测试很难做到正确,因为 JIT 可以通过优化来完成如此美妙的事情。再一次,这就是 点 :JIT 通过优化做了如此美妙的事情,public 和私有方法一样快。
I am just curious, even though it is more or less by a tiny itty-bitty amount.
虽然好奇是件好事......如果你在编程时开始考虑到这种事情,那么:
您可能会浪费大量时间寻找不需要的微优化,
你的代码很容易无法维护,因为你牺牲了良好的设计原则,
你甚至冒着让你的代码更少效率*的风险。
* - 它可以这样进行。 1) 您花费大量时间调整代码以在测试平台上快速 运行。 2) 当你在生产平台上运行,你发现硬件赋予你不同的性能特征。 3) 您升级 Java 安装,新的 JVM 的 JIT 编译器以不同方式优化您的代码,或者它有一堆新的优化被您的调整 抑制 。 4) 当您 运行 在真实世界的工作负载上编写代码时,您会发现作为调整基础的假设是无效的。