Memory optimization tool, enjoy meta mode


Before we know what the meta model is, let's look at a question.

demand

We need to create an arsenal that reproduces and produces various types of bullets. Each bullet needs a model description and its own unique logo. Then I implemented one with the following code.

Abstract specification of bullet

/**
 * Bullet specification, abstract object
 */
public abstract class BulletSpecification {

    /**
     * Diameter, mm
     * @return
     */
    public abstract double getCaliber();

    /**
     * Length, mm
     * @return
     */
    public abstract double getLength();

    /**
     * shape
     * @return
     */
    public abstract String getShape();

    @Override
    public String toString() {
        return "Type:" + this.getClass().getSimpleName() + ",Caliber:" + getCaliber() + ",Length:" + getLength() + ",Shape:" + getShape();
    }
}

Various specific specifications

/**
 * .22 BB Cap
 * @author skyline
 */
public class BBCap22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 7;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

/**
 * .22 CB Cap
 * @author skyline
 */
public class CBCap22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 11;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

/**
 * .22 Long
 * @author skyline
 */
public class Long22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 15;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

/**
 * .22 LR
 * @author skyline
 */
public class LR22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.7;
    }

    @Override
    public double getLength() {
        return 15;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

/**
 * .22 Short
 * @author skyline
 */
public class Short22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 11;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

/**
 * .22 Stinger
 * @author skyline
 */
public class Stinger22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 18;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}
/**
 * .22 Win
 * @author skyline
 */
public class Win22 extends BulletSpecification {
    @Override
    public double getCaliber() {
        return 5.59;
    }

    @Override
    public double getLength() {
        return 27;
    }

    @Override
    public String getShape() {
        return "Rim,S";
    }
}

Bullets and bullet factories

/**
 * bullet
 * @author skyline
 */
public class Bullet {
    private final String id;
    private final BulletSpecification specification;

    public Bullet(BulletSpecification specification) {
        this.id = UUID.randomUUID().toString();
        this.specification = specification;
    }

    public String getId() {
        return id;
    }

    public BulletSpecification getSpecification() {
        return specification;
    }

    @Override
    public String toString() {
        return "Bullet{" +
                "id='" + id + '\'' +
                ", specification=" + specification +
                '}';
    }
}
/**
 * Bullet factory
 * @author skyline
 */
public class BulletFactory {

    public Bullet createBullet(Class<? extends BulletSpecification> specificationClazz) {
        Bullet bullet = null;
        try {
            BulletSpecification bulletSpecification = specificationClazz.newInstance();
            bullet = new Bullet(bulletSpecification);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return bullet;
    }
}

Bullet production

public class FlyWeightMain {
    public static void main(String[] args) {
        BulletFactory factory = new BulletFactory();
        Class<? extends BulletSpecification>[] types = new Class[]{BBCap22.class, CBCap22.class, Long22.class, LR22.class, Short22.class, Win22.class, Stinger22.class};
        List<Bullet> bullets = new ArrayList<>();
        for (Class<? extends BulletSpecification> type : types) {
            for (int i = 0; i < 100000; i++) {
                Bullet bullet = factory.createBullet(type);
                bullets.add(bullet);
                System.out.println(bullet.toString());
            }
        }
    }
}

problem

In fact, from the perspective of program operation, there is no problem with the above set of code. It can also end normally by running it directly. In order to highlight the problem, I added some restrictions to the program - Xmx115M -Xms115M, and then we'll take a look.

From the screenshot, we can see that GC is very crazy. The final GC takes about 5.3 seconds. So is there room for optimization?

reflection

After analyzing the objects, we found that each bullet object contains two parts, as shown in the following figure:

One is the id of the bullet, and this is different for each bullet. Another is the specification of bullets. This specification information contains a lot of data. In fact, it is not necessary to create a new specification information for each bullet. Bullets of the same specification can share a specification information object. In this way, we can save a lot of memory space.

reform

According to the above thought, I modified the program. After the transformation, the program is as follows:

The specification information used by bullets of the same specification is cached in the specificationMap. Each time a bullet is created, the specification information is obtained from the specificationMap. If the specification information does not exist, it is created again. Let's take a look at the GC after the transformation:

From the perspective of GC, it is obviously much faster than the first version of the program. This is the benefit of the meta model.

Sharing element mode

Meta sharing mode is mainly used to reduce the number of objects created to reduce memory consumption and improve performance. This type of design pattern belongs to structure pattern, which provides a way to reduce the number of objects and improve the object structure required for application.
Meta pattern attempts to reuse existing homogeneous objects. If no matching object is found, a new object is created.

String constant pool

The most typical application of shared element mode at the JVM level is string constant pool. String can be used as a constant because the string itself is final, and the char [] in the string is also final, that is, once the string is new, it cannot be changed again.

Next, let's look at the following classic code:

/**
 * String constant pool
 */
public class StringMain {
    public static void main(String[] args) {
        String str1 = new String("a");
        String str2 = "a";
        String str3 = str1.intern();
        String str4 = new String("a").intern();
        System.out.println(str1 == str2); //false
        System.out.println(str2 == str3); //true
        System.out.println(str3 == str4); //true
        System.out.println(str4 == str1); //false
    }
}

summary

  1. Meta sharing mode, sharing metadata information. It can save memory and improve efficiency.
  2. The meta sharing mode also contains the idea of pooling. For example, the above specificationMap is an object pool.
  3. Because the meta pattern needs to encapsulate the new method, it is generally used together with the factory pattern.
  4. String constant pool is the shared element mode implemented at the JVM level. Thread pools can also be seen as meta patterns.

Keywords: Design Pattern

Added by dhope on Mon, 03 Jan 2022 14:20:14 +0200