เกี่ยวกับการรั่วไหลของหน่วยความจำ

webman เป็นเฟรมเวิร์กที่ทำงานในหน่วยความจำที่ทำให้เรา ต้องให้ความสนใจกับสถานการณ์การรั่วไหลของหน่วยความจำสักเล็กน้อย อย่างไรก็ตามนักพัฒนาที่ทำงานกับเฟรมเวิร์กนี้ไม่ต้องกังวลมากเกินไป เนื่องจากการรั่วไหลของหน่วยความจำเกิดขึ้นภายใต้เงื่อนไขที่รุนแรงมาก และสามารถหลีกเลี่ยงได้ง่าย ประสบการณ์ในการพัฒนา webman จะมีความคล้ายคลึงกับการพัฒนาจากเฟรมเวิร์กแบบดั้งเดิม ไม่จำเป็นต้องทำการจัดการหน่วยความจำเพิ่มเติม

提示
โปรแกรม monitor ที่มาพร้อมกับ webman จะติดตามการใช้หน่วยความจำของกระบวนการทั้งหมด หากหน่วยความจำที่ใช้ถึงค่าที่กำหนดใน memory_limit ในไฟล์ php.ini โปรแกรมจะทำการรีสตาร์ทกระบวนการนั้นๆ โดยอัตโนมัติอย่างปลอดภัย เพื่อทำการปล่อยหน่วยความจำ ซึ่งจะไม่มีผลกระทบต่อธุรกิจในระหว่างนั้น

การกำหนดการรั่วไหลของหน่วยความจำ

เมื่อจำนวนคำขอเพิ่มขึ้นอย่างต่อเนื่อง หน่วยความจำที่ใช้โดย webman ก็จะ เพิ่มขึ้นอย่างไม่จำกัด (หมายถึง เพิ่มขึ้นอย่างไม่จำกัด) อาจสูงถึงหลายร้อย MB หรือมากกว่านั้น ซึ่งแบบนี้ถือเป็นการรั่วไหลของหน่วยความจำ
หากหน่วยความจำมีการเพิ่มขึ้น แต่ไม่มีการเพิ่มขึ้นอีกต่อไป จะไม่ถือว่าเป็นการรั่วไหลของหน่วยความจำ

โดยทั่วไปแล้วการใช้หน่วยความจำหลายสิบ MB ถือเป็นเรื่องปกติ เมื่อกระบวนการจัดการกับคำขอที่มีขนาดใหญ่หรือดูแลการเชื่อมต่อจำนวนมาก หน่วยความจำที่ใช้โดยกระบวนการแต่ละตัวอาจสูงถึงหลายร้อย MB ก็เป็นเรื่องที่พบบ่อย หน่วยความจำที่ใช้ส่วนนี้อาจไม่ถูกส่งคืนให้กับระบบปฏิบัติการทั้งหมด เนื่องจากอาจถูกเก็บไว้เพื่อการใช้งานซ้ำ ดังนั้นอาจพบว่าหลังจากการจัดการกับคำขอขนาดใหญ่ หน่วยความจำจะเพิ่มขึ้นโดยไม่ถูกปล่อยคืน ซึ่งถือเป็นปรากฏการณ์ปกติ (สามารถเรียกใช้ gc_mem_caches() เพื่อปล่อยหน่วยความจำที่ว่างบางส่วน)

การรั่วไหลของหน่วยความจำเกิดขึ้นได้อย่างไร

การรั่วไหลของหน่วยความจำจะเกิดขึ้นภายใต้เงื่อนไขสองประการต่อไปนี้:

  1. มีอาร์เรย์ที่มี อายุยืนยาว (หมายถึงอาร์เรย์ที่มีอายุยืนยาว อาร์เรย์ทั่วไปไม่มีปัญหา)
  2. และอาร์เรย์ที่มี อายุยืนยาว นี้จะขยายตัวอย่างไม่จำกัด (ธุรกิจจะใส่ข้อมูลเข้าไปเรื่อย ๆ โดยไม่เคลียร์ข้อมูล)

หากเงื่อนไข 1 และ 2 ได้ถูกต้องพร้อมกัน (หมายถึงต้องได้ถูกต้องพร้อมกัน) ก็จะเกิดการรั่วไหลของหน่วยความจำ ถ้าไม่ได้ตามเงื่อนไขดังกล่าวหรือเพียงแค่ตรงตามเงื่อนไขใดเงื่อนไขหนึ่ง จะไม่ถือว่ามีการรั่วไหลของหน่วยความจำ

อาร์เรย์ที่มีอายุยืนยาว

ใน webman อาร์เรย์ที่มีอายุยืนยาวรวมถึง:

  1. อาร์เรย์ที่ใช้คำสำคัญ static
  2. อาร์เรย์ที่เป็นคุณสมบัติของ singleton
  3. อาร์เรย์ที่ใช้คำสำคัญ global

注意
webman อนุญาตให้ใช้ข้อมูลที่มีอายุยืนยาว แต่ต้องมั่นใจว่าข้อมูลในนั้นมีจำนวนที่จำกัด ไม่สามารถขยายเพิ่มขึ้นอย่างไม่จำกัด

ต่อไปนี้เป็นการยกตัวอย่างเพื่ออธิบาย

อาร์เรย์ static ที่ขยายตัวอย่างไม่จำกัด

class Foo
{
    public static $data = [];
    public function index(Request $request)
    {
        self::$data[] = time();
        return response('hello');
    }
}

อาร์เรย์ $data ที่กำหนดโดยใช้คำสำคัญ static เป็นอาร์เรย์ที่มีอายุยืนยาว และในตัวอย่าง $data จะขยายตัวขึ้นเรื่อยๆ ตามจำนวนคำขอที่เข้ามา ส่งผลต่อการรั่วไหลของหน่วยความจำ

อาร์เรย์คุณสมบัติ singleton ที่ขยายตัวอย่างไม่จำกัด

class Cache
{
    protected static $instance;
    public $data = [];

    public function instance()
    {
        if (!self::$instance) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    public function set($key, $value)
    {
        $this->data[$key] = $value;
    }
}

รหัสที่เรียกใช้

class Foo
{
    public function index(Request $request)
    {
        Cache::instance()->set(time(), time());
        return response('hello');
    }
}

Cache::instance() คืนค่า Cache singleton ซึ่งเป็นอินสแตนซ์ของคลาสที่มีอายุยืนยาว ถึงแม้ว่า $data ของมันจะไม่ได้ใช้คำสำคัญ static แต่เนื่องจากคลาสนี้มีอายุยืนยาว ดังนั้น $data ก็ถือเป็นอาร์เรย์ที่มีอายุยืนยาวเช่นกัน โดยการเพิ่มข้อมูลในอาร์เรย์ $data ที่มีคีย์ที่แตกต่างกันเรื่อยๆ ทำให้โปรแกรมใช้หน่วยความจำมากขึ้นส่งผลให้เกิดการรั่วไหลของหน่วยความจำ

注意
หาก Cache::instance()->set(key, value) เพิ่มคีย์ที่มีจำนวนจำกัด จะไม่มีการรั่วไหลของหน่วยความจำ เนื่องจากอาร์เรย์ $data ไม่ได้ขยายเพิ่มขึ้นอย่างไม่จำกัด

อาร์เรย์ global ที่ขยายตัวอย่างไม่จำกัด

class Index
{
    public function index(Request $request)
    {
        global $data;
        $data[] = time();
        return response($foo->sayHello());
    }
}

อาร์เรย์ที่กำหนดโดยใช้คำสำคัญ global จะไม่ถูกเก็บคืนเมื่อฟังก์ชันหรือเมธอดของคลาสเสร็จสิ้น ดังนั้นมันจึงถือเป็นอาร์เรย์ที่มีอายุยืนยาว โค้ดด้านบนจะเกิดการรั่วไหลของหน่วยความจำเมื่อจำนวนคำขอเพิ่มขึ้นอย่างต่อเนื่อง เช่นเดียวกันอาร์เรย์ที่กำหนดในฟังก์ชันหรือเมธอดโดยใช้คำสำคัญ static ก็ถือเป็นอาร์เรย์ที่มีอายุยืนยาว หากมันขยายตัวอย่างไม่จำกัดก็จะนำไปสู่การรั่วไหลของหน่วยความจำ เช่นเดียวกับ:

class Index
{
    public function index(Request $request)
    {
        static $data = [];
        $data[] = time();
        return response($foo->sayHello());
    }
}

คำแนะนำ

แนะนำให้นักพัฒนาไม่ต้องกังวลเกี่ยวกับการรั่วไหลของหน่วยความจำมากเกินไป เพราะมันเกิดขึ้นน้อยมาก หากเกิดขึ้นอย่างโชคร้าย เราสามารถใช้การทดสอบแรงกดดันเพื่อตรวจหาว่าส่วนใดของโค้ดก่อให้เกิดการรั่วไหล เพื่อระบุปัญหา แม้ว่า นักพัฒนาจะไม่สามารถหาจุดรั่วไหลได้ แต่บริการ monitor ที่มาพร้อมกับ webman จะรีสตาร์ทกระบวนการที่มีการรั่วไหลของหน่วยความจำอย่างปลอดภัยในเวลาที่เหมาะสม เพื่อปล่อยหน่วยความจำ

หากท่านต้องการหลีกเลี่ยงการรั่วไหลของหน่วยความจำอย่างเต็มที่ สามารถอ้างอิงตามคำแนะนำด้านล่างนี้

  1. พยายามหลีกเลี่ยงการใช้การกำหนดค่าอาร์เรย์ด้วยคำสำคัญ global และ static หากจำเป็นต้องใช้ ให้แน่ใจว่าอาร์เรย์นั้นไม่สามารถขยายตัวได้เรื่อย ๆ
  2. สำหรับคลาสที่ไม่คุ้นเคย ควรหลีกเลี่ยงการใช้ singleton และให้ใช้คำสั่ง new ในการสร้างอ็อบเจ็กต์ หากต้องการใช้ singleton ควรตรวจสอบว่ามีคุณสมบัติที่เป็นอาร์เรย์ที่ขยายตัวอย่างไม่จำกัดหรือไม่