In the process of program development, there are often a large number of repeated objects. Creating objects repeatedly often requires the VIRTUAL machine to allocate more than one block of memory to store these objects, resulting in a waste of memory. If the flow peak, and for the stability of the system is also a challenge. Enjoy yuan mode can solve the problem we say, “enjoy yuan” what is to enjoy yuan, is to share the object. Duplicate objects do not need to be created repeatedly, saving memory space.
Enjoy the definition and characteristics of yuan
The definition of share mode puts forward two kinds of boundary requirements, one is internal state, one is external state.
- Internal state refers to the information shared by the object, which is stored inside the metadata and does not change with the change of the environment.
- An external state is a tag that an object can depend on, changes with the environment, and is not shareable.
This will be demonstrated in subsequent code cases.
The structure of the share schema
The main roles in Enjoy mode are as follows.
- Abstract member role (Flyweight) : is the base class of all concrete member metaclases. It is the public interface that the concrete member specification needs to implement. The external state of non-member member is passed in the form of parameters through methods.
- Concrete Flyweight role: Implements the public interface in the abstract share.
- Unsharable Flyweight role: an external state that cannot be shared. It is injected as a parameter into the methods associated with the specific Flyweight.
- Flyweight Factory role: The Factory of the privilege object, responsible for creating and managing the privilege role.
Xiangyuan Mode structure diagram:
Code case: here enumerates a case of the next period, is also to see the Internet cited a typical yuan model of the case. Now we’re going to simulate renju, and there are only two kinds of pieces, so let’s say we have a board that’s 10×10, and let’s say we’re going to design renju. Suppose we include the color and coordinates of the pieces in the properties of the pieces. So the number of pieces we need to create is 2x10x10 possibilities. If we use the design idea of sharing elements, the same shared role chess pieces, is not only need two objects, the specific coordinates are extracted from the role can not be shared, when using the specified coordinates. Let’s look at the code implementation:
- Abstract Player character (chess piece)
// The chessman is abstract
public interface ChessPieces {
/ / to play chess
void down(Point point);
}
Copy the code
- Play black for role 1
public class BlackChessPieces implements ChessPieces{
// Pawn color
private String color;
public void setKey(String color) {
this.color = color;
}
@Override
public void down(Point point) {
System.out.println("Current black position X:"+ point.getX() + ", Y:"+ point.getY()); }}Copy the code
- Specifically, play dollar role 1 white
public class WhiteChessPieces implements ChessPieces {
// Pawn color
private String color;
public void setKey(String color) {
this.color = color;
}
@Override
public void down(Point point) {
System.out.println("Current black position X:"+ point.getX() + ",Y:"+point.getY()); }}Copy the code
- Non-privileged role The privileged role references the object coordinates
public class Point {
/ / the abscissa
private Integer x;
/ / ordinate
private Integer y;
public Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}
public Integer getX(a) {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY(a) {
return y;
}
public void setY(Integer y) {
this.y = y; }}Copy the code
- Enjoy yuan factory class
public class ChessPiecesFactory {
// Share the meta-object resource pool
private Map<String, ChessPieces> pool = new HashMap<>();
public ChessPieces getChessPieces(String key){
ChessPieces poolChessPieces = pool.get(key);
if(poolChessPieces! =null) {
System.out.println("Successful acquisition" + key + "Chess");
return poolChessPieces;
}else {
if (key.equals("Black")) {
BlackChessPieces blackChessPieces = new BlackChessPieces();
blackChessPieces.setKey(key);
pool.put(key, blackChessPieces);
System.out.println("Black chess piece has been created");
return blackChessPieces;
}else {
WhiteChessPieces whiteChessPieces = new WhiteChessPieces();
whiteChessPieces.setKey(key);
pool.put(key, whiteChessPieces);
System.out.println("White chess piece has been created");
returnwhiteChessPieces; }}}public int poolSize(a){
returnpool.size(); }}Copy the code
- Use square to simulate two people playing chess
public class Client {
public static void main(String[] args) {
ChessPiecesFactory chessPiecesFactory = new ChessPiecesFactory();
String black = "Black";
String white = "White";
// If you want to simulate two people next stage small A, small B
//A: Sunspot goes first
ChessPieces chessPieces = chessPiecesFactory.getChessPieces(black);
Point point1 = new Point(0.0);
chessPieces.down(point1);
System.out.println("Number of pieces:"+ chessPiecesFactory.poolSize());
/ / B: white
ChessPieces chessPieces2 = chessPiecesFactory.getChessPieces(white);
Point point2 = new Point(0.1);
chessPieces2.down(point2);
System.out.println("Number of pieces:"+ chessPiecesFactory.poolSize());
/ / A: black
ChessPieces chessPieces3 = chessPiecesFactory.getChessPieces(black);
Point point3 = new Point(1.1);
chessPieces3.down(point3);
System.out.println("Number of pieces:"+ chessPiecesFactory.poolSize()); }}Copy the code
After obtaining chess pieces each time, view the number of objects in the current shared resource pool.
Execution Result:
Current black position X:0, Y:0Number of pieces:1The white piece has been created at the current black position X:0,Y:1Number of pieces:2Successfully obtain the current position of black piece X:1, Y:1Number of pieces:2
Copy the code
As you can see, the second time we play black, the number of moves hasn’t changed.
Case Structure Diagram:
Advantages and disadvantages of the share mode
Advantages:
- Only one copy of the same object is saved. This reduces the number of objects in the system, reducing the pressure on the memory caused by fine-grained objects in the system, and reducing the risk of memory insufficiency.
Disadvantages:
- In order for objects to be shareable, some state that cannot be shared needs to be externalized, which increases the complexity of the program.
- The boundary between inside and outside is not easy to control. If the reference point is changed later, it also needs to be modified, which violates the open and close principle.
- The factory class of the meta-schema holds the meta-object all the time, even if no other object is in use, the garbage collector will not collect it, resulting in a waste of memory.
Usage scenarios of the Share mode
The system design process needs to take this into account if an object is created frequently and there are common parts to share. And create multiple objects, may cause memory pressure, at this time need to consider using the share mode to optimize.
- There are a lot of similar objects in the system.
- Fine-grained objects all have relatively close external state and the internal shared state is consistent.
Use of meta schema in JDK source code
1. Use the meta schema in Integer.
public class Test {
public static void main(String[] args) {
Integer i1 = 88;
Integer i2 = 88;
Integer i3 = 188;
Integer i4 = 188; System.out.println(i1==i2); System.out.println(i3==i4); }}Copy the code
The first output is true and the second output is false. Open Integer’s source code to find out why this is the case. If we use a primitive type definition variable, such as Integer i1 = 88 in the example above, I1 = integer.valueof (88); i1 = integer.valueof (88); On the other hand, if the wrapper type of variable i1 receives int I = i1 from the base data type int, the underlying operation is int I = i1.intValue(). With that in mind, look at the source code for Integer:
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
/ / the valueOf method
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// Note the variable IntegerCache
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if(integerCacheHighPropValue ! =null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache(a) {}}Copy the code
The default static block has been loaded with [-128, 127] data into the Integer cache[] array. This results in our case if the value is in the range, it is directly in the cache, and if it is outside the range, we will create a new Integer object. As you can see, by default there’s a minimum number, a maximum value assigned to 127, and we can also change the maximum value through the parameter Settings to suit our own needs. As shown in the source code
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
Copy the code
Can be added on the launch parameters – Djava. Lang. Integer. IntegerCache. High = 255.
2. Let’s look at the following code:
String str1 = "a";
String str2 = "a";
String str3 = new String("a");
String str4 = new String("b") + new String("c");
str4.intern();
String str5 = "bc";
System.out.println(str1 == str2); //true
System.out.println(str1 == str3); //false
System.out.println(str4==str5);//true
Copy the code
If you guessed all the results correctly, you have a good understanding of String’s underlying storage. Back to the essence of the problem, why str1 == str2 is true. Str1 == str3 results in false. Strings defined by String str1 = “a” are placed in the constant pool space of JVM memory. So when we define str2, we refer directly to strings that already exist in the constant pool. String str3 = new String(“a”); The address of Str3 points to the address of the new heap space. All STR1 and Str3 are not the same object. String str4 = new String(“b”) + new String(“c”); And String str5 = “BC “; Str4.intern (); str4.intern(); . This operation performs two steps to determine if the string is in the constant pool. If not, the string value is stored in the constant pool. In JDK7, the string is stored in the constant pool. So when we create Str5, we point directly to the same object through the existing address, so we compare the object with the same address. Str4 ==str5, the result is true.
Summary: In the implementation of the Java String class, the JVM allocates a storage area for String constants, a pool of String constants, similar to IntegerCache in Integer. Unlike IntegerCache, however, it does not create objects that need to be shared in advance. Instead, string constants are created and cached as needed during the execution of the program.
expand
1. Share mode and pooling technology to see a lot of articles online, you can use share mode to achieve thread pool, database connection pool, etc. The sharing in the sharing mode is the same object can be used in many places, and we said that the thread pool, database connection pool and other technologies, here sharing is not shared by threads; A database connection pool, for example, is a batch of database connections initialized, used by a thread, and then returned. Threads are not shareable, and the shared part of this article is the same object. The entire life cycle is shared by all users. The thread pool, the database connection pool, is implemented through the share mode. You need to consider the “shared difference” between the two.