在 PHP 中使用静态方法和属性会占用更少的内存吗?

Posted

技术标签:

【中文标题】在 PHP 中使用静态方法和属性会占用更少的内存吗?【英文标题】:Does using static methods and properties in PHP use less memory? 【发布时间】:2012-10-10 14:04:16 【问题描述】:

我正在开发一个每秒可查看数十个并发用户的 Web 应用程序。我有一个类将在同一页面加载中多次实例化。在那个类中,我有一些属性在每个对象中总是相同的,所以我正在考虑将这些属性声明为static,以减少在实例化此类的多个实例时将使用的内存在同一页面请求期间。

这样做会不会因为 php 只能存储一次静态属性的值而为该应用程序使用更少的内存?这样做会在并发用户之间节省内存,还是只在每个 PHP 进程中节省内存?

这对方法有什么作用?如果这意味着对象可以回收相同的方法,那么如果你想节省内存,为什么不能将类的所有方法都声明为静态?

我不太明白为什么以及何时将属性或方法声明为静态的,但我明白将它们声明为静态允许在不实例化类对象的情况下访问它们(这感觉就像一个 hack ...这些方法和属性应该在其他地方...不是吗?)。我对static 声明影响内存使用的方式特别感兴趣,以努力使我的 Web 服务器上的内存使用尽可能低……总的来说,我对正在发生的事情有更好的了解。

【问题讨论】:

我不明白为什么它被关闭了。这个问题非常具体,肯定有一个准确且无可争议的答案。 非常好的和准确的问题,但我想指出 PHP 中函数的可见性(源自 OOP)对于您的问题“如果这意味着对象可以回收相同方法,那么如果你想节省内存,为什么不将一个类的所有方法都声明为静态?”。有些函数可能是静态的,有些函数不能是静态的——可以说,获取/设置类变量的函数取决于类初始化过程。主要限制是您当时正在处理的对象(实例化)。 【参考方案1】:

当您将类方法/变量声明为静态时,它绑定到类并由类共享,而不是对象。从内存管理的角度来看,这意味着当类定义加载到堆内存中时,会在那里创建这些静态对象。当在堆栈内存中创建类的实际对象并且完成静态属性的更新时,指向包含静态对象的堆的指针被更新。这确实有助于减少内存,但幅度不大。

从编程范式来看,人们通常选择使用静态变量来获得架构优势,而不是内存管理优化。换句话说,当一个人想要实现单例或工厂模式时,可能会像您提到的那样创建静态变量。它提供了更强大的方法来了解在“类”级别发生的事情,而不是在“对象”级别发生的事情。

【讨论】:

【参考方案2】:

在 PHP 中使用静态方法和属性会占用更少的内存吗?

大概吧。但是,你为什么要搞乱你的 OOP 架构呢?

重要吗?

可能不会。需要记忆的是 PHP 本身。我坚信删除几个字节,因为您使用静态方法不会有所作为。相反,不要加载无用的模块。例如,如果您不使用 GD,请不要加载它。激活缓存以减少调用 PHP 的次数。

【讨论】:

这里有一些很好的建议,尤其是减少我在 PHP 中加载的内容。谢谢。 @T.BrianJones 当然我可以详细说明这一点,但这超出了您的问题范围。但你可以研究 APC、memcache、... @MathieuImbert:你能解释一下“如果你不使用 GD,就不要加载它”。你的意思是在php.ini配置中根本不加载GD扩展? @MarcoDemaio 从你的 php.ini 中删除它是的。 @MathieuImbert:这是一个有趣的观点,我想知道是否可以通过使用 .htaccess 和 suPHP 来卸载加载到主 php.ini 服务器文件中的模块。我在共享主机环境中,无法不更改主 php.ini 文件。【参考方案3】:

静态方法调用在多次迭代中更快,但静态方法并不能真正节省内存。

如果您要声明的类没有任何需要对每个对象实例唯一的属性,那么您可以将每个方法和属性声明为静态。但是,如果您有需要绑定到每个对象的属性,那么静态方法就没有帮助。原因是在静态方法内部,没有对$this 的引用,因此您无法从静态方法中引用对象属性。

阅读Static Keyword 以更好地理解这一点。

【讨论】:

实际上正常的方法更快(我怀疑内存使用可能有类似的相关性)。额外的成本是对象的实例化。基本上,如果您在同一个对象上执行超过 4 个方法,那么总体上使用一个对象变得更加省时。还有一点是普通方法的次要问题是 OOP(关于多态性和依赖注入的所有 BS)和静态方法是过程编程范式的一部分。【参考方案4】:

我不是 PHP 内存管理方面的专家,但我会说你不会节省太多。您是否节省以及节省多少取决于某些方面:

对象的大小(创建实例时,该对象保存了多少其他数据)。 您创建的对象数量。

尤其是对象的数量很重要。如果您只有一个实例,您可以节省 50%。在这种情况下:

案例 A - 静态:您不实例化对象,而只是使用存储在内存中的类定义。但是每个 REQUEST 都会加载类定义,这意味着内存中的类定义数量与并发请求的数量相同。

案例 B - 实例:除了案例 A,您还为每个请求提供了此对象的实例,因此您的这部分软件的内存使用量增加了一倍。

最后:如果你更容易使用静态参数而不是每次都实例化类,你应该使用静态方式。但不要期望太多的内存提升。

【讨论】:

【参考方案5】:

查看静态与单例测试:http://moisadoru.wordpress.com/2010/03/02/static-call-versus-singleton-call-in-php/

注意:由于某些原因,*** 没有显示 multilne 主题,所以我添加了一张图片。

Number of runs  Singleton call time (s)     Static call time (s)
100             0.004005                    0.001511
1,000           0.018872                    0.014552
10,000          0.174744                    0.141820
100,000         1.643465                    1.431564
200,000         3.277334                    2.812432
300,000         5.079388                    4.419048
500,000         8.086555                    6.841494
1,000,000       16.189018                   13.696728

在此处查看更多详细信息:https://***.com/a/3419411/260080

【讨论】:

可以在实例化的类对象中访问声明为静态的属性 - 试试看【参考方案6】:

如果您共享数据,请使用静态数据。它更快并且为您节省了对象实例化的过程。当您需要单个入口点时,单例会胜过静态。大约 1 周前,我在博客上介绍了这一点。

【讨论】:

感谢保利的解释。我错了吗?我是在无耻地宣传我的商业网站吗?保利让 SO 变得更美好!【参考方案7】:

一般来说,是的。静态方法和属性使用较少的内存。但是,差异非常小。

更有趣的是performance difference between static and non-static methods。

【讨论】:

【参考方案8】:

我刚刚改进了 Stanislav 链接的基准以使其生效:

https://3v4l.org/rDpVv

Results for PHP 7.4.1:

Runs by case: 500000 
Memory usage at start: 426,320

Run                                 Duration    %       Memory
Dynamic                             0.0594      30%     496
Dynamic instantiated                0.0917      46%     0           #
Dynamic instantiated stored         0.1994      100%    48,967,472  # slowest
Storage only                        0.0422      21%     16,781,392
Cost of instations only when stored 0.1572      79%     32,186,O8O  # cost of stored instatiations minus storage cost (diff of 2 previous lines)
Static                              0.0870      44%     0           # equivalent to dynamic with instantiation
Singletons with many getInstance    0.1213      61%     376
Singletons with one getInstance     0.0669      34%     320         # equivalent to "Dynamic"
Functions assigning $GLOBALS        0.0605      30%     0           # more than 2 times longer than using "global"
Functions assigning a global        0.0458      23%     32          # fastest. 32bits allocated? probably passed by copy... odd
Functions with $counter by ref      0.0707      35%     0           # slow for functions
Functions with $counter static prop 0.0524      26%     0

备注:

“修改全局的函数”最快,占 23% “实例化,存储然后调用动态方法”是最长的,所以 100% 存储实例会消耗大量内存和 21% 的总时间 “通过 ref 将 $counter 作为参数传递”几乎是“修改全局函数”的 2 倍 调用修改静态属性的函数超级快,几乎是调用静态方法的一半。搞笑 MyClass::call() 花费了 Singleton::getInstance()->call() 时间的 75%,但花费了 $mySingleton->call() 的 133% MyClass::call() 的成本与 (new MyClass)->call() 一样多 “静态”在成本上等同于“动态实例化非存储”。真的很有趣!

关于开发实践的结论(2020 年 1 月生效):

永远不要使用 $GLOBALS,'global $myVar' 非常快(并且分配 32 位?) 仅使用全局变量和函数进行编程是最快的 PHP 吗?老派摇滚! 为大量方法调用存储一个实例然后删除它是最佳选择。 避免存储大量实例。 “实例化调用”和“静态调用”成本相同

干杯

PS:由于限制,即使结果不是 100% 稳定,我也无法进行更多的跑步(我看到整个板凳的一些刷新有 20% 的变化) PS 2:如果你想禁用 3v4l.org 的缓存,只需在代码中任意位置添加一个空格即可

【讨论】:

以上是关于在 PHP 中使用静态方法和属性会占用更少的内存吗?的主要内容,如果未能解决你的问题,请参考以下文章

Apache和Nginx的区别

Apache和Nginx的区别

调用垃圾回收会导致程序在java中使用更少的堆内存

原来PHP对象比数组用更少的内存

Memcache 与 Java 内存

如何让 MySQL 使用更少的内存?