[[204255]] Problem description: - Since last week, iOS developers in the team have been complaining about the computer being particularly slow. A careful student found that because Xcode takes up about 6-7G of memory, and some Macs only have 8G of memory, the memory is full and causes the slowdown.
- Some of my classmates’ Macs have 16G memory, like me (sarcastic face), and I didn’t feel any lag because there was enough memory.
- But this problem affects the team's development efficiency, so it needs to be solved.
Memory comparison: After bathing, changing clothes, burning incense, killing processes, and clearing caches, we pulled the adjacent 812 version code and 816 version code and compiled them separately, and got the conclusion: - 812 debugging, occupies 2G memory
- 816 debugging, occupies 6.8G memory
Make complaints: - We refuse to accept this data. We have two complaints:
- If the code randomly requests memory, then the memory should be exploded in the simulator or real device, not in Xcode.
- If the current version adds 100,000 lines of code (actually less than), and the total code volume does not increase by more than 10%, how is it possible that Xcode's memory usage can double?
- So we think that this must be Apple's fault, and we are not responsible.
But no matter whose fault it is, it must be triggered by the code or configuration, so the analysis needs to continue. Analysis method selection: - There are two analysis methods before us:
- Find the code: Use binary search to compile versions with different dates, find the commit that caused the problem, and determine which change caused it.
- Find the memory: Analyze what the increased memory is, and analyze where the problem lies based on the increased content.
- If you use method 1, it takes 15 minutes to compile the code once. If the problem is caused by a certain line of code, it will take a day to find it. If the problem is caused by a combination of multiple lines of code, it will take longer. And even if you find the code, you may not know the principle.
- So I choose **Method 2**, if it doesn't work, I will go back to Method 1
Analysis steps: When I run it, I find: - 812 The memory is within 1G when the code is first opened, and 2G when compiling and running. 2G when closing Xcode and opening it again
- 816 The memory is within 1G when opening the code for the first time, 6G when compiling and running, and 6G when opening after closing Xcode
After closing Xcode and reopening it, Xcode is not running at this time, so it is assumed that it is doing one thing: reading the cache Cache files: - As we all know, Xcode will compile a new project very slowly, but the second time it compiles it will be very fast. That's because it saves the compilation results in a cache file. The second time it compiles the read-only file without compiling it will naturally be faster.
- The cache files are stored in the directory "/Users/your username/Library/Developer/Xcode/DerivedData"
- The comparison of cache files between versions 812 and 816 is as follows:
- Initially, we can see that the number of cache files is the same, but the size is very different. So the next step is to find out: which one is bigger?
- After some searching, I found that each class generates three files:
.o file: binary object file, no more explanation .d file: a text file that records all file paths that the class depends on .dia file: unknown binary file, but it is the one that becomes larger - dia has some size increase, and some size does not. I tried to open it with binary tool and read it, and I was surprised:
Here comes my complaint again: - Who is it! Stand up! **Wrote a 4G warning!**
Continue analysis: So what exactly caused the warning? Faced with thousands of .dia files, I was devastated. - Fortunately, I talked to a good friend, who happened to have done a code warning scan and found that 816 only had 107 more warnings in a certain group of codes compared to 812, while other groups remained unchanged. Moreover, they were nonnull-related warnings, which were not important so I did not pursue it.
- We found 107 warning codes and checked the commit records, which were before everyone reported the lag. It seemed to be this. We solved the warning, clean and recompiled, and the problem was solved.
Although the problem is solved, there are still two questions left: - Why did I submit 107 warnings?
- There are only 107 warnings. Why does it cause the memory usage to soar? Why is it ok when we still have hundreds of warnings?
Question 1: - There is only one line of code that causes 107 warnings
- Apple's unspoken rule for nonnull-related warnings is this:
A new feature provided since Xcode6 allows you to declare whether a function parameter is required (nonnull) or optional (nullable). This will make the code more rigorous and we recommend using it. Compatible with old code: There is no nonnull/nullable declaration in the entire header file, and the compilation is fine High requirements for new code: once a nonnull/nullable is added to the code, the rest of the code must also be added, otherwise each other interface will have a warning - So, the code involved in this case is an old utility class with 107 functions. A new line of code added nonnull. So 107 warnings were generated.
Question 2: For example, there are three classes ABC - Ah there is a warning, and the .dia file will have the following information:
insert '_Nullable' if the pointer may be null insert '_Nonnull' if the pointer should never be null - Am file absolute path
- Ah file absolute path
- Which line of the Am file references Ah? There is a warning
- Warning in Ah
- The warning description is: pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)
- Two ways to fix:
- In short, a warning message is about 1k
- If B references A, then B's .dia file contains all the above information, as well as multiple B file paths, that is, B's description information exceeds A's.
- If C references B, and B references A in its header file, then C's description information exceeds B's.
so - In engineering, the 107 warning file has a size of about 130k.
- The total number of directly and indirectly referenced files is about 2500, and each file is over 130k. The file size is about 350M.
- In addition, the simulator has two CPU architectures (i386/x86_64), which will generate two files, and there is also an aggregated dgph file in the cache. And the memory space occupied by the file after it is structured in memory.
- Therefore, it is understandable that the memory usage eventually doubled to 4G.
in conclusion: - Do not ignore warnings, especially warnings in header files, which will be referenced in many places and cause the description information to be too large.
- Try not to import header files in header files, as this will cause excessive references and amplify the problem.
Follow-up: - Version 818 has fixed all nonnull issues in core. Warnings will be cleared gradually in the future.
- The memory usage after fix is shown in the figure
PS: Is this an Apple bug? I think it's just a self-destructive mistake. [This article is an original article by 51CTO columnist "Alibaba Official Technology". Please contact the original author for reprinting.] Click here to read more articles by this author |