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

标签 php json

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

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

但在某些情况下我得到

PHP Fatal error: Allowed memory size of xxx bytes exhausted (tried to allocate 32 bytes) in ...

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

1- 无论我设置什么尺寸,都可能仍然太小。 2- 如果我将内存大小设置为“无限”,那么我可能会冒着杀死我的服务器的风险。

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

这可能吗?

最佳答案

如果它们耗尽了您服务器的内存,您一定会收到一些大量的 JSON 响应。以下是一个 1 MB 文件的一些指标,该文件包含一个多维关联数组(包含准备进入具有不同数据类型的三个 MySQL 表的数据)。

当我 include 并将文件作为数组加载到内存中时,我的内存使用量达到 9 MB。如果我使用 file_get_contents() 获取原始数据,它会按预期占用 1 MB 内存。然后,PHP 数组与数据的 strlen() 的比例大约为 1:9(最初使用 var_export() 输出)。

当我运行 json_encode() 时,峰值内存使用量没有增加。 (PHP 以 block 为单位分配内存,因此通常会有一些开销,在本例中足以包含 JSON 的字符串数据;但它可能会增加一个 block 。)生成的字符串形式的 JSON 数据需要 670 KB。

当我使用 file_get_contents 将 JSON 数据加载到字符串中时,预计需要 0.75 MB 的内存。当我在其上运行 json_decode() 时,它需要 7 MB 的内存。然后,我会将 JSON-data-bytesize 解码为 native PHP 数组或对象 的最小比例为 1:10,以满足 RAM 要求。

要在解码之前对 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 文件的值(value)数据作为 12000 个随机字符串作为基本关联数组,加载时内存使用量仅为 5 MB; 1:5的比例。
    1. 如果我创建一个 1 MB 的文件作为一个类似的关联数组,其中一半的条目是带有数字索引的字符串数组,内存使用量为 7 MB,比例为 1:7。

因此您的实际 RAM 里程可能会有很大差异。另外请注意,如果您循环传递大量数据并做一些这样那样的操作,您的内存使用量可能会增加很多(或呈指数增长,取决于您的代码经济)高于 json_decode() 单独导致的结果。

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

关于php - 'safe' json_decode( ,,, ) 以防止耗尽内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34993444/

相关文章:

javascript - 如何解析json对象来更新HANA表?

java - 日期格式映射到 JSON Jackson

python - 如何分解 Pandas 中的嵌套 json 结构?

php - Laravel:如何在 Form POST 中使用参数以在 Route::post 中使用?

php - 有条件地加入 Laravel Fluent

php - 在 php 中发布标签值或表格单元格值或隐藏文本值

php - PHP 中的 "SHOW COLUMNS"查询未显示第一行

php - 查询更新选择在mysql中不起作用?

java - 将维基百科页面的内容获取到 Android

php - Laravel 5 如何返回带有 response()->json() 的模板?