Unity3d memory management-Object pool)

Source: Internet
Author: User
Starting with a simple object pool class

The concept behind the object pool is actually very simple. We store objects in a pool and use them again when needed, instead of instantiating a new object each time. The most important feature of the pool is that the design pattern of the Object pool allows us to obtain a "new" object, whether it is a new object or a circular object. This mode can be implemented using the following simple lines of code:

public class ObjectPool<T> where T : class, new(){    private Stack<T> m_objectStack = new Stack<T>();    public T New()    {        return (m_objectStack.Count == 0) ? new T() : m_objectStack.Pop();    }    public void Store(T t)    {        m_objectStack.Push(t);    }}

It is very simple and well reflects the core of this model. If you do not quite understand "where T", it doesn't matter. It will be explained later. How to use it? You just need to find the expression using the new operator, for example:

void Update(){    MyClass m = new MyClass();}

Replace it with new () and store ().

ObjectPool<MyClass> poolOfMyClass = new ObjectPool<MyClass>();void Update(){    MyClass m = poolOfMyClass.New();    // do stuff...    poolOfMyClass.Store(m);}
Increase complexity

I am a loyal believer in conciseness, but for now the objectpool class may be too simple. If you search for the object pool class library implemented in C #, you will find that many of them are quite complex. Let's pause and think about what we need and what we don't need in a general object pool:

  • Many types of objects must be reset in some cases before they are reused. At least, all member variables must be set to the initial value. This can be implemented in the pool without user processing. When and how to reset:
    • Resetting is immediate (for example, resetting when the object is stored) or delayed (for example, resetting after the object is used again ).
    • Resetting is managed by the pool (for example, transparent to objects in the pool) or a class that declares the pool object.
  • In the above example, the poolofmyclass pool object needs to be displayed in the class-level scope. Obviously, when we need a pool of other types of objects, we need to declare a new one. Perhaps we can achieve a transparent to users.
  • Create an objectpool to manage all types of pools.
  • Some object pool class libraries manage too many types of terrible resources (such as memory, database connections, Game objects, and external assets ). This undoubtedly increases the Code complexity of the Object pool.
  • Some types of resources are very precious (such as database connections). The pool needs to display the upper limit and provide a security measure for failed object allocation;
  • When many objects in the pool are rarely used, you may need to contract the function (either automatic or forced ).
  • Finally, the pool can be shared by multiple threads, so it must be thread-safe.

 

Which of the following are necessary? Your answer may be different from mine, but allow me to elaborate on my point of view:

  • Resetting is required. However, as you will see below, I have not forced to process the reset logic in the pool or in the management class. You may need both of them, and I will show you the two versions in the subsequent code.
  • UnityForcibly restrict Multithreading. You can define worker threads in the main thread, but only the main thread can call the unity API. In my experience, we do not need to implement the pool to support multithreading.
  • Personally, I don't mind declaring a new pool for a type each time. The optional solution is to useSingleton Mode: Create a new object pool and place it in the dictionary of the storage pool. The dictionary is placed in a static variable. For secure use, you need to implement your object pool to support multithreading. But none of the object pools I see are 100% secure.
  • In this article, I focus on memory processing. Other types of resource pools are also important, but they are beyond the scope of this article. This greatly reduces the following requirements:
    • You do not need a maximum value for limits. If your game uses too many resources, you are in trouble and the Object pool cannot save you.
    • We can also assume that no other process is waiting for you to release the memory as soon as possible. This means that the reset can be delayed, and the contraction function is not required.
A basic pool with initialization and reset)

The revised version is as follows:

public class ObjectPool<T> where T : class, new(){    private Stack<T> m_objectStack;    private Action<T> m_resetAction;    private Action<T> m_onetimeInitAction;    public ObjectPool(int initialBufferSize, Action<T>        ResetAction = null, Action<T> OnetimeInitAction = null)    {        m_objectStack = new Stack<T>(initialBufferSize);        m_resetAction = ResetAction;        m_onetimeInitAction = OnetimeInitAction;    }    public T New()    {        if (m_objectStack.Count > 0)        {            T t = m_objectStack.Pop();            if (m_resetAction != null)                m_resetAction(t);            return t;        }        else        {            T t = new T();            if (m_onetimeInitAction != null)                m_onetimeInitAction(t);            return t;        }    }    public void Store(T obj)    {        m_objectStack.Push(obj);    }}

This implementation is simple and straightforward. The parameter T is specified as "where T: Class, new ()", which means there are two restrictions. First, T must be a class (after all, only the reference type needs to be obejct-pool); second, it must have a non-argument constructor.

The constructor takes the maximum possible value of the pool as the first parameter. The other two are optional closures. If values are input, the first closure is used to reset the pool, and the second is used to initialize a new object. Besides constructors, objectpool <t> has only two methods: New () and store (). Because the pool uses a latency policy, the main task is new (). The new and recycled objects are either instantiated or reset. These two operations are implemented through the passed-in closure. The following describes how to use a pool:

Class someclass: monobehaviour {private objectpool <list <vector3> m_pooloflistofvector3 = // 32 indicates the maximum number of new objectpools <list <vector3> (32, (list) ==>{ list. clear () ;}, (list) =>{// the initial capacity is 1024 list. capacity = 1024;}); void Update () {list <vector3> listvector3 = m_pooloflistofvector3.new (); // do stuff m_pooloflistofvector3.store (listvector3 );}}
Managed self-Reset pool (a pool that lets the managed Type reset itself)

The above Object pool implements basic functions, but it is still flawed. It separates the initialization and resetting objects in the object definition. To some extent, it violatesEncapsulationPrinciples. CauseTight coupling, Which should be avoided as much as possible. In someclass, there is no real alternative, because we cannot modify the definition of list <t>. However, when you use a custom class, you can implement the iresetable interface instead. The corresponding objectpoolwithreset <t> does not need to specify two closures (please note that I have left it for flexibility ).

Public interface iresetable {void reset ();} public class objectpoolwithreset <t> where T: Class, iresetable, new () {private stack <t> m_objectstack; private action <t> m_resetaction; private action <t> m_onetimeinitaction; Public objectpoolwithreset (INT initialbuffersize, Action <t> resetaction = NULL, Action <t> onetimeinitaction = NULL) {m_objectstack = new stack <t> (initialbuffersize); m_resetaction = Resetaction; m_onetimeinitaction = onetimeinitaction;} public T New () {If (m_objectstack.count> 0) {T = m_objectstack.pop (); // reset t by yourself. reset (); If (m_resetaction! = NULL) m_resetaction (t); Return t;} else {T = new T (); If (m_onetimeinitaction! = NULL) m_onetimeinitaction (t); Return t ;}} public void store (t obj) {m_objectstack.push (OBJ );}}
Collective reset pool (a pool with collective reset)

Some types do not need to be retained in a series of frames. They only expire before the frame ends. In this case, we can store all pooled objects in the pool at an appropriate time. Now, we rewrite the pool to make it simpler and more efficient.

Public class objectpoolwithcollectivereset <t> where T: Class, new () {private list <t> m_objectlist; private int m_nextavailableindex = 0; private action <t> m_resetaction; private action <t> m_onetimeinitaction; Public objectpoolwithcollectivereset (INT initialbuffersize, Action <t> resetaction = NULL, Action <t> onetimeinitaction = NULL) {m_objectlist = new list <t> (initialbuffersize); m_resetaction = Resetaction; m_onetimeinitaction = onetimeinitaction;} public T New () {If (values <m_objectlist.count) {// an allocated object is already available; just reset it t = m_objectlist [values]; m_nextavailableindex ++; If (m_resetaction! = NULL) m_resetaction (t); Return t;} else {// No allocated object is available t = new T (); m_objectlist.add (t); m_nextavailableindex ++; if (m_onetimeinitaction! = NULL) m_onetimeinitaction (t); Return t ;}} public void resetall () {// reset the index m_nextavailableindex = 0 ;}}

Compared with the original objectpool <t>, the changes are quite large. Regardless of the class signature, we can see that store () has been replaced by resetall (), and is called only once when all allocated objects need to be put into the pool. Inside the class, stack is replaced by list, which stores references to all allocated objects (including objects in use. We can also track the index of recently created or released objects in the list, so that new () can know whether to create a new object or reset an existing object.

Extended reading (non-translated part)

Address: http://www.gamasutra.com/blogs/WendelinReich/20131127/203843/C_Memory_Management_for_Unity_Developers_part_3_of_3.php

The above describes the basic principles and implementation of objectpool. A more mature plug-in is recommended below --Poolmanager,This plug-in is very powerful, so I dare to sell it so expensive, 30 USD... A Chinese cool man has written a good tutorial. If you are interested in shoes, refer to the unity3d Research Institute's poolmanager plug-in.

Unity3d memory management-Object pool)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.