一尘不染

'safe'json_decode(,,,)以防止耗尽内存

json

在我的应用中,我经常调用一个返回json字符串的外部api。

$url = 'api.example.com/xyz';
$blah = json_decode( file_get_contents( $url ) );

但是在某些情况下

PHP致命错误:内存中已耗尽xxx字节(尝试分配32字节)的内存大小…

我无法控制外部API,当然可以增加php的内存,但这有一些缺点。

1-无论我设置的大小,仍然可能太小。2-如果将内存大小设置为“ infinite”(无限),则可能会导致服务器被杀。

理想情况下,我想在调用json_decode(…)之前先“检查”字符串是否导致内存耗尽。

那可能吗?


阅读 240

收藏
2020-07-27

共1个答案

一尘不染

如果它们设法耗尽服务器的内存,则必须获得大量的JSON响应。以下是一些指标,这些指标包含一个包含多维关联数组的1
MB文件(包含准备输入到具有不同数据类型的三个MySQL表中的数据)。

当我include和文件作为数组加载到内存中时,我的内存使用量达到9
MB。如果我使用来获取原始数据file_get_contents(),则它需要1
MB的内存。然后,PHP数组strlen()与数据的比率大约为1:9 (最初以输出var_export())。

当我运行时json_encode(),峰值内存使用不会增加。(PHP在块中分配内存,因此通常会产生一些开销,在这种情况下,足够包含JSON的字符串数据;但是它可能会使您增加一个块。)作为字符串的结果JSON数据需要670
KB。

当我将JSON数据加载file_get_contents到字符串中时,它需要0.75
MB的内存。当我json_decode()在其上运行时,它需要7 MB的内存。然后,对于RAM需求,我将解码的 JSON数据字节大小
本机PHP数组或对象 的最小比率为1:10 。

要在解码之前对JSON数据进行测试,您可以执行以下操作:

if (strlen($my_json) * 10 > ($my_mb_memory * 1024 * 1024)) {
    die ('Decoding this would exhaust the server memory. Sorry!');
}

…其中$my_json是原始JSON响应,$my_mb_memory是分配的RAM,已转换为字节以与传入数据进行比较。(当然,您也可以使用intval(ini_get('memory_limit'))整数来获取内存限制。)

如下所述,RAM的使用也将取决于您的数据结构。相比之下,还有一些快速测试用例,因为我很好奇自己:

    1. 如果我创建一个整数为1-60000的一维数组,则保存的PHP数组大小为1 MB,但是RAM的峰值使用量在10.5和12.5 MB之间(好奇振荡),或比例为1:12-ish。
    1. 如果我将一个1 MB文件的值的数据作为12000个随机字符串作为基本关联数组创建,则加载时的内存使用量仅为5 MB。比例为1:5。
    1. 如果我创建一个价值1 MB的文件作为相似的关联数组,其中一半条目是带有数字索引的字符串数组,则内存使用率为7 MB,比例为1:7。

因此,您的实际RAM里程可能会相差很大。
还应注意,如果您绕一圈传递大量数据并进行一些操作,那么您的内存使用量可能会比json_decode()单独导致的使用量高很多(或成倍地增加,具体取决于您的代码经济性)。

要调试内存使用情况,您可以在代码中使用memory_get_usage()和/或memory_get_peak_usage()间隔很长时间来记录或输出代码不同部分中使用的内存。

2020-07-27