Unity3d 游戏优化 mono等

2023-11-12

 

///

GC问题

1、C#变量分为两种类型:值类型和引用类型,值类型分配在栈区,引用类型分配在堆区,GC关注引用类型

  2、GC卡顿原因:堆内存垃圾回收,向系统申请新的堆内存

  3、GC触发条件:堆内存分配而当内存不足时、按频率自动触发、手动强行触发(一般用在场景切换)

  4、GC负面效果:内存碎片(导致内存变大,GC触发更加频繁)、游戏顿卡

  5、GC优化方向:减少GC次数、降低单次GC运行时间、场景切换时主动GC

  6、GC优化策略:减少对内存分配次数和引用次数、降低堆内存分配和回收频率

  7、善用缓存:对有堆内存分配的函数,缓存其调用结果,不要反复去调用

  8、清除列表:而不要每次都去new一个新的列表

  9、用对象池:必用

  10、慎用串拼接:缓存、Text组件拆分、使用StringBuild、Debug.Log接口封装(打Conditional标签)

  11、警惕Unity函数调用:GameObject.name、GameObject.tag、FindObjectsOfType<T>()等众多函数都有堆内存分配,实测为准

  12、避免装箱:慎用object形参、多用泛型版本(如List<T>)等,这里的细节问题很多,实测为准

  13、警惕协程:StartCoroutine有GC、yield return带返回值有GC、yield return new xxx有GC(最好自己做一套协程管理)

  14、foreach:unity5.5之前版本有GC,使用for循环或者获取迭代器

  15、减少引用:建立管理类统一管理,使用ID作为访问token

  16、慎用LINQ:这东西最好不用,GC很高

  17、结构体数组:如果结构体中含有引用类型变量,对结构体数组进行拆分,避免GC时遍历所有结构体成员

  18、在游戏空闲(如场景切换时)强制执行GC

#Unity3D optimization tips

Some code allocates memory when running in the editor and not on the device. This is due to Unity doing some extra error handling so possible error messages can be output in the console. Much of this code is stripped during build. It's therefore important to profile on device, and do it regularly.

Optimizations often makes code more rigid and harder to reason. Don't do it until you really need to.

When profiling, wrap methods or portions of a method in Profiler.BeginSample(string name) and Profiler.EndSample() to make them easier to find in the profiler.

public class SomeClass {
	
	public void Update() {
		// Misc. code here ...

		Profiler.BeginSample("Heavy calculations");
		// Heavy calculations
		Profiler.EndSample();

		// More misc.code here
	}
}

##Enum as key in a dictionary

Enums can be used as keys in a dictionary, but they require some setup to avoid littering the heap.

Mono will do boxing on the enum when comparing dictionary keys. Looking up an item in a dictionary via ContainsKey, TryGetValue or using array notation on the dictionary will allocate objects on the heap.

The boxing happens because of Mono's default equal comparer, and can be fixed with a custom IEqualityComparer. The downside is that you need to implement an equal comparer for each of your enums. See the discussion on Stack Overflow and the blog post from Somasim for a more in-depth explanation.

Enums are backed by integers and can therefore be cast to and from ints. You can make a utility method that handles the casting for you, like this:

public Something GetSomething(SomeEnum e) {
	return someDictionary[(int)e];
}

With a method like this, you can safely redefine your dictionary to use an int as key, avoid the allocation problems and still have the readability of using an Enum.

###References

##Struct as key in a dictionary

Just like with enums, structs will get boxed and put on the heap when used as keys in a dictionary. The solution is to have the struct implement IEquatable, override GetHashCode(), Equals() and the == and != operators to use strongly typed equality comparisons.

using System;
using UnityEngine;

public struct MyStruct : IEquatable<Int3> {
	public readonly int x;
    public readonly int y;
    public readonly int z;

    public MyStruct(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public override bool Equals(System.Object obj) {
        if (!(obj is MyStruct)) {
            return false;
        }

        MyStruct i = (MyStruct)obj;
        return (x == i.x) && (y == i.y) && (z == i.z);
    }

    public bool Equals(MyStruct i) {
        return (x == i.x) && (y == i.y) && (z == i.z);
    }

    public override int GetHashCode() {
        return x ^ y ^ z;
    }

    public static bool operator == (MyStruct a, MyStruct b) {
        return a.x == b.x && a.y == b.y && a.z == b.z;
    }

    public static bool operator != (MyStruct a, MyStruct b) {
        return !(a == b);
    }
}

###References

##Lists

Iterating a list using foreach creates a new instance of an Enumerator. Mono discards the enumerator when done iterating, thus causing unnecessary memory allocations. Tests shows that some cases don't cause allocations, such as using foreach on a generic list and dictionary. See the link to Jackson Dunstan's blog.

When developing, default to using foreach because it is easier to read and work with. Replace with a for loop only when you see the need for it, which you'll discover during optimization. If you do replace foreach with for, cache the length of the array before starting the iteration.

Any object references you make during iteration should use objects defined outside the loop to prevent allocations, like this:

SomeClass someInstance = null;
for (int i=0; i<10; i0++) {
	someInstance = someList[i];
}

In any case, preallocate slots for arrays, lists and dictionarys when instantiating them. Pick the maximum number of items you expect to fill the list with.

When instantiating a list without preallocating slots, you allocate only space for the list itself. When you call list.Add(), the application will find new memory space to hold the entire list -including the newly added item, copy the old list to the new space, add the item, and point the list reference to the new memory address. You can easily avoid this by preallocating multiple slots.

private List<T> list;

void Start() {
	list = new List<T>(25);
}

###Fun facts

Did you know that Dictionary.Keys and Dictionary.Values are not lists but classes? Simply accessing a dictionary's Keys property allocates some bytes on the heap.

###References

##Branching

A modern CPU tries to stay ahead of the script's execution point and precalculates as many lines ahead as possible. When reaching an if statement it uses branch prediction to either decide early on what branch to take, or calculate both branches to be safe. The more complex and harder to predict your branch is, the more likely the CPU will precalculate both branches.

If your code features lots of branches, and is hard to rewrite, consider using a behaviour tree instead. Make sure the behaviour tree implementation doesn't use any if statements, and that none of the behaviour tree's nodes use any.

Most branches can be avoided using simple arithmetics and lists. I managed to reduce the runtime of a function from 22ms to 2ms by avoiding a few branches. It was a busy function, getting called around 2.900 times per update.

Avoiding branches is also a good practice because it often results in code that's easier to read and maintain.

###Example

Call one function when a is equal to b, and another when they're not.

public class Example01 {
	
	public void Run(int a, int b) {
		if (a == b) {
			Oneway();
		} else {
			TheOther();
		}
	}
	
	private void OneWay() {}
	
	private void TheOther() {}
}

We can avoid the branch using an array of functions, and some simple arithmetics to figure out which branch to chose.

public class Example01 {
	
	private Action[] actions;
	
	public Example01() {
		actions = new Action[2] { OneWay, TheOther };
	}
	
	public void Run(int a, int b) {
		int branch = Min(Math.Abs(b - a), 1);
		actions[branch]();
	}
	
	private void OneWay() {}
	
	private void TheOther() {}
	
	private int Min(int a, int b) {
		return (a + b - Mathf.Abs(a-b)) * 0.5f;
	}
	
	private int Max(int a, int b) {
		return (a + b + Mathf.Abs(a-b)) * 0.5f;
	}
}

To calculate the array index of the correct function, we first get the absolute value of the difference between a and b. This limits the range from 0 to the maximum value of Int32. Lastly we clamp that value between 0 and 1 using the Min method. I left the Max method in there just so you can see how it compares to Min.

When a equals b, the difference is 0, and we use the first function in the actions array. For all other values the second function is used.

##Strings

Don't use strings for anything but displaying text in the UI or logging.

It's better to cache strings in a list and pass a pointer to the string to the UI element, than passing a raw string.

public class StringCaching {
	
	private UIElement uiElm;
	private string[] labels;
	
	public StringCaching() {
		labels = new string[2] {
			"Hello world",
			"Hello there"
		};
	}
	
	// This is bad
	public void RawString() {
		uiElm.label = "Hello world";
	}
	
	// This is good
	public void CachedString() {
		uiElm.label = labels[0];
	}
}

When doing string concatenation, use a StringBuilder. A StringBuilder can be reused, but be aware that emptying a StringBuilder causes memory allocations. In .net 2.5 you don't have access to StringBuilder's Clear method. Your only options are using Replace or setting the Length to 0. These methods causes memory allocations that seem to be proportional to the size of the string.

If you have a string with a constant length, like a timer that shows minutes and seconds (MM:SS), you should try using a character array instead.

public class Timer {

	private char[] digits;
	private UIElement uiElm;
	
	public Timer() {
		digits = new char[5];
		digits[2] = ':';
	}
	
	public void Update() {
		digits[0] = minutesTen;
		digits[1] = minutes;
		digits[3] = secondsTen;
		digits[4] = seconds;
		uiElm = new string(char);
	}
}

Depending on the length of your string, and how much you need to change, using a character array might not be the solution for you. Test using the profiler to see what causes the least amount of allocations in your case.

Don't use strings for comparisons. If you're searching for an object named something, see if you can use an Enum, or an integer ID instead.

##Recycling

Use object pools so that you can reuse frequently used objects without the cost of a new allocation.

Make objects reusable by implementing a Reset method. A good practice is to use the Reset method in the constructor. That way you can be sure that as long as the object is in the right state when initialized, it will also be when resetting.

public class Poolable {
	
	private int value;
	
	public Poolable() {
		Reset();
	}
	
	public void Reset() {
		value = 0;
	}
}

Objects like game entities, sound effects, even messaging objects are good candidates for object pools.

##Meshes

When changing vertex colors of a mesh, use MeshRenderer's additionalVertexStreams to add a per mesh instance override. This way you'll prevent creating new instances of each mesh.

##Delegates

When declaring delegates, default to an empty function, or cache an empty function to avoid the extra null check. Simplifes code and is better for branch prediction.

public class DelegateCaching {
	
	private Action nullAction = () => {};
	private Action callback;
	
	public DelegateCaching() {
		callback = nullAction;
	}
	
	public void Update() {
		callback();
	}
}

In this example we create an empty function using a lambda expression and point callback to it. NullAction could also be private method as longs as it has the correct signature. In Update() we don't need to check if callback is null.

Be however careful with the lambdas. Mono will cache your lambdas, which makes them safe to use. But can easily create a closure by making a reference to an external variable or function, and that breaks caching. Mono will in those cases generate a new class on the fly, each time the lambda is called. The newly generated class pollutes the heap and stresses the garbage collector.

Don't do like this:

public class LambdaCaching {
	
	private List<MyObject> list;
	
	public LambdaCaching() {
		list = new List<MyObject>(10);
		list.Add(new MyObject());
		// etc.
	}
	
	public void Update() {
		int l = list.Count;
		for (int i=0; i<l; i++) {
			Callback(i, myObj => {
				myObj.DoSomething(i);
			});
		}
	}
	
	private void Callback(int i, Action<MyObject> callback) {
		callback(list[i]);
	}
}

The bad happening in this example is the referencing of the i int inside the lambda expression. Mono will create a new class with the i int as a private field, ten times each update.

##Prefabs loaded via Resources

Unload prefabs from memory after loading them from Resources.

public class Loader {

	public void Load(string asset) {
		GameObject prefab = Resources.Load<GameObject>(asset);
		GameObject instance = GameObject.Instantiate(prefab);
		Resources.UnloadAsset(prefab);
	}
}

When loading prefabs from Resources you always instantiate them, otherwise you'll start to make changes to the prefab itself. Since instantiating duplicates the object there's no reason to keep the prefab around.

##Caching array length

Array.Length doesn't need caching, but List<T>.Count does. Check this out: http://jacksondunstan.com/articles/3577

##Data structures

All though not directly a tips for optimization, the data structure will largely affect how much of your code you actually can optimize. It's a task well worth spending time on to get right.

TL;DR: Keep it simple!

Data should be easy to work with. The API should help you get the right data with the least amount of input, at the shortest time possible. Keep the API simple to make it easy to learn, but add as many function overrides as you need to make it flexible.

Decide on a set of basic data blocks and design the API around them. In a game of pool a ball could be a basic data block. The table holds a list of balls, and the pockets are functions that accepts a ball as parameter.

You should be able to pass the data blocks you get from the API back to it without the need to transform them. In an adventure game, when a character picks up an item from the ground, it shouldn't need to create a new instance of an object just to be able to add it to the inventory.

When deciding on a data structure, prioritize the simplest first. If you don't need a resizable list, choose an Array instead. If you need a more complex structure, add helper methods to make it easier for yourself and your peers to work with the data.

Keep scalability in mind when designing the data structure, but don't overdo it. The more scalable the data is, the harder it is to work with. Scalable data means the possibility to add more data without breaking the API or the structure.

Whenever you want to add more properties to your data, think it through. Can you get the information you're looking for from other properties? Can you merge the new property with another to keep the API simple? In a platformer game the characters probably have a health property. Instead of adding a boolean called isDead, can you instead check for health == 0? Always go for the least amount of data that you need, and pick the data that gives the most meaning.

##Good practice

Gain control over the garbage collector by doing large cleanups when the game is not doing anything important, like in a menu scene, and avoid creating allocations when the game is running.

Keep classes small and simple. It'll make it easier to reason the code and fix bugs. When class gets larger than 300 lines, split it. In cases where you need to make a large class, split it using a partial class.

Splitting code into smaller functions makes it easier to reuse code and identify bottlenecks during profiling. Reusable functions also reduces the need to write more code, which results in less time spent hunting bugs.

Write code in decoupled, self contained classes with methods that accepts as few parameters as possible and have no hard dependencies to other classes to make it easier to run isolated tests. Your development cycle will be slow if you always need to compile the entire project and play up to a specific point in the game to test a certain feature. Make it easy to either mock data or load data from disk.

##More stuff on Unity optimizations

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity3d 游戏优化 mono等 的相关文章

  • Pycharm中修改注释文本的颜色(详细设置步骤)

    下面是在Pycharm中设置注释文本颜色的详细步骤 下面是修改前后对比 修改前注释行的颜色 修改后注释行的颜色 以上就是Pycharm中修改注释文本颜色的详细步骤 希望能帮到你
  • 小程序真机调试连接本地服务器进行调试

    小程序连接本地服务器 开发小程序时经常会遇到需要连接本地服务器进行调试的时候 但是总是连接不上 这里就说一下本菜鸟连接本地服务器的方法 第一步 把下图红框的地方勾选住 好多方法都得选这一步 第二步 设置里面代理按图中勾选 第三步是连接的方法
  • JavaScript避免使用return跳出多重循环从而继续执行函数;使用break跳出多重for循环

    一 先来看一下使用break仅跳出一层for循环的用法 const foo function for let i 1 i lt 3 i for let j 1 j lt 3 j if i 2 break console log 输出j的值
  • Mac上使用GPU加速训练模型

    文章目录 前言 使用GPU 前言 上一篇文章中我介绍了使用pytorch的一个完整模型训练套路 其中没有使用gpu 如果要使用gpu的话 win上我们可以使用cuda mac上可以使用mps 而我自己是mac电脑 需要进行如下修改 使用GP
  • ubuntu下docker配置国内镜像源

    既然都看到这篇文章了 就不解释为什么需要配置国内镜像源了 直接上步骤 此文使用ubuntu环境为Ubuntu 18 04 4 LTS 使用 sudo vim etc docker daemon json 命令新建或编辑文件 输入以下内容 r
  • 大话设计模式C++实现-第23章-命令模式

    一 UML图 二 概念 命令模式 Command 将一个请求封装为一个对象 从而使你可用不同的请求对客户进行参数化 对请求进行排队或记录请求日志 以及支持可撤销的操作 三 说明 角色 1 Command类 用来声明执行操作的接口 2 Con
  • 漫谈 SLAM 技术(下)

    转自 https zhuanlan zhihu com p 135958593 3 视觉SLAM系统关键问题 结合上述介绍的SLAM系统 我们从以下几个方面分析视觉SLAM系统需要考虑的关键问题 1 图像信息使用 视觉SLAM方法根据使用图
  • 模块""可能与您正在运行的Windows版本不兼容。检查该模块是否与regsvr32.exe的x86或x64版

    本人最近在研究mencoder 转换视频格式 发现转换rmvb需要 1 把drv43260 dll拷贝到系统的system32文件夹下 2 开始 gt 运行 gt regsvr32 drv43260 dll 来自 http topic cs
  • C++容器之 vector map set查找元素

    前面两篇基本上讲解容器的增加删除 其实现实世界中对数据的查找才是最大的需求 下面主要围绕着容器的查找来讲解 首先 由于vector没有实现find 方法 只能使用algorithm提供的find 方法 所以 直接在vector查找节介绍al
  • 多线程编程

    Linux线程概述 内核线程和用户线程 线程是程序中完成独立任务的完整执行序列 即一个可调度的实体 根据运行环境和调度者身份 线程分为内核线程和用户线程 内核线程 在有的系统上也称为LWP 轻量级进程 运行在内核空间 由内核调度 用户线程
  • 概率在计算机学中的应用,概率统计在计算机中的应用

    概率统计在计算机中的应用 一 综述 研究自然界中随机现象统计规律的数学方法 叫做概率统计 又称数理统计方法 概率论 是根据大量同类随机现象的统计规律 对随机现象出现某一结果的可能性作出一种客观的科学判断 对这种出现的可能性大小做出数量上的描
  • 交换机ACL配置

    交换机ACL配置 实验要求 PC3能ping通R3和R4 PC2能ping通R3和R4 R3和R4全网互通 通过ACL使PC2不能ping通PC3 先给每个接口配置ip PC3 192 168 5 3 24 192 168 5 254 PC
  • ngx_http_ssl_module

    ngx http ssl module 模块 语法 ssl 在 从 默认值 ssl off 背景 http 服务器 支持HTTPS协议为给定的虚拟服务器 推荐使用 ssl 参数的 听 指令而不是 这个指令 语法 ssl buffer siz
  • 哈哈,太真实了!除了《颈椎康复指南》,还有这 9 本书

    点击上方 Java后端 选择 设为星标 优质文章 及时送达 作者 sivagao 链接 https github com sivagao 本文罗列的这些书籍封面其实是各种典型的反模式 不过它们真的是非常常见以至于大家都习以为常了 从 Sta
  • Go面试题专题(一):聊聊你理解的Golang defer关键字

    defer关键字是我们工作中经常用到的go语言特性 也是面试官比较青睐的一个知识点 今天通过这篇文章带各位道友彻底掌握它 面试题文档下链接点击这里免积分下载 go语言入门到精通点击这里免积分下载 文章目录 defer两大特性 defer与r
  • vue中element-ui实现表单根据不同下拉框进行动态表单校验

    vue中element ui实现表单根据不同下拉框进行动态表单校验 我们想实现的功能如下 请看效果 话不多说我们上代码 html部分
  • linux cd命令详解

    Linux cd 英文全拼 change directory 命令用于切换当前工作目录 使用方式 cd dirName 其中 dirName 为目录名称 可为绝对路径或相对路径 若目录名称省略 则切换至home 目录 也表示为 home 目
  • Android平台生成二维码(by google.zxing)

    查了大部分的资料 发现android平台下生成二维码的例子都是使用谷歌的zxing类 因此仿照某一个帖子编写了一个demo进行测试 仿照的帖子 https blog csdn net myname kk article details 77
  • 使用 Hexo 搭建静态个人博客与绑定个人域名

    1 安装Git 下载并安装Git 可以选择淘宝 Git for Windows 镜像 https npm taobao org mirrors git for windows 2 安装Node js 下载安装Node js Node js

随机推荐

  • SpringMVC关于Validform实时校验身份证的作为账户的问题

    此地址上有相关案例 http validform rjboy cn 看不懂别怪我 前端代码 例如 div class f fl item ifo item sfz div
  • C#知识结构

    对于一个工作多年的程序员而言 接口 反射 索引器 事件 委托这些耳熟能详的词汇 提起来别说多简单了 但是让老司机坐在那一个人拿起一支笔 把脑海中对C 知识结构进行梳理一下 大抵是写不了多内容的 原因是什么呢 是遗忘 当然不是 每天面对代码的
  • mkdir函数-linux

    mkdir函数 头文件库 include
  • Heron 编译错误:no such package ‘@org_apache_thrift_libthrift//jar’

    错误 ERROR heron heron metricsmgr src java BUILD 5 1 no such package org apache thrift libthrift jar Failed to fetch Maven
  • The 19th ZCPC -G. Easy Glide

    Grammy is playing a boring racing game named Easy Gliding The game s main content is to reach the destination as fast as
  • 安全工具杂烩

    20201103 本来想单独列出来一个文章来记录每个工具 但是发现并没有那么多精力 这里仅仅记录一下看到的一些不错的工具 sdnewhop grinder 据其描述 这个是一个通过shodan或者censys来获取主机信息的工具 是不是跟一
  • 阿里云服务器一直提示安全事件如何解决

    介绍 这几天一直收到阿里云官方的短信和邮箱提示阿里云安全事件提醒 阿里云的官方的客服也打电话询问过我需不需要帮助 由于我的阿里云服务器没有用于商业用途 只是学习的时候使用 所有也就决定自己解决了 影响 由于最近比较忙 就没有怎么注意阿里云短
  • 433MHz工业级无线数传通信模块

    433MHz工业级无线数传通信模块 无线RS232 RS485透明传输 距离1 3000米 DTD465系列工业无线数传模块采用最先进的电子和无线通信技术 能为众多的工业与应用提供高性能 中等距离和可靠数据传输的低成本解决方案 它的工业级电
  • 计算机date时间和‘千年虫事件’

    目录 一 千年虫事件 1 千年虫事件 名词解析 2 应对2000年计算机问题的解决方法 二 Unix Linux 2038问题 Linux系统的几种时间 1 时间戳 date 2 UTC时间和本地时间 timedatectl 3 避免因时间
  • 日精注塑机联网

    不改造程序的话 日精支持输出CSV和txt数据作为其他软件的接口 改造后可以支持63协议 在软件层面日精也有专用的软件 可以看到其实设备厂家提供的软件功能已经非常丰富了 但这类软件最大的缺点是只能自己家的机器使用 要想其他家也兼容进来 既要
  • 【星海随笔】计组数学小课堂

    计算机组成原理 https www bilibili com video BV1ps4y1d73V p 8 16的负一次方既为1 16 16 1 16进制转换为10进制 例如 5 8 5 16 1 8 16 1 十进制转N进制 则除以N 然
  • Transformers中文本生成方法model.generate()参数解释

    本博客仅作为记录 参考 LLM 大语言模型 解码时是怎么生成文本的 爱码网
  • python字符串中所有符合条件的索引

    使用re库中的finditer import re s 1111ah11111ah test re finditer ah s print i for i in test
  • mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹

    Android MVP架构从入门到精通 真枪实弹 一 前言 你是否遇到过Activity Fragment中成百上千行代码 完全无法维护 看着头疼 你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况 你是否遇到过用MVC架构写的项目进行
  • VMware Workstation 16 Player 安装Centos 7

    环境准备 VMware Workstation 16 Player 官方下载 https www vmware com products workstation player workstation player evaluation ht
  • Cesium 源码解析 Model(一)

    Cesium中对于3DTiles会解析成Model 特别是3DTile中的B3DM 过程主要是对gltf在Cesium中是如何解析并生成绘制命令的 content model new Model gltf gltfView gltf数据 c
  • 全球数字治理白皮书 附下载

    当今世界 科技革命和产业变革日新月异 数字经济蓬勃发展 深刻改变着人类生产生活方式 对各国经济社会发展 全球治理体系 人类文明进程影响深远 在经济全球化遭遇逆流 保护主义 单边主 义上升的背景下 数字化驱动的新一轮全球化仍蓬勃发展 已成为助
  • QT学习笔记(七)

    第12章 输入与输出 Qt提供了读写字节块的设备类QIODevice QIODevice类是抽象的 无法被实例化 一般是使用它的子类 它包括如下子类 其中 QProcess QTcpSocket QUdpSocket QSslSocket都
  • Java 优先级队列

    文章目录 Java 优先级队列 PriorityQueue简介 继承关系 PriorityQueue示例 Comparable比较器 Comparable接口 Comparator比较器 Comparator接口 底层原理 Java 优先级
  • Unity3d 游戏优化 mono等

    GC问题 1 C 变量分为两种类型 值类型和引用类型 值类型分配在栈区 引用类型分配在堆区 GC关注引用类型 2 GC卡顿原因 堆内存垃圾回收 向系统申请新的堆内存 3 GC触发条件 堆内存分配而当内存不足时 按频率自动触发 手动强行触发