🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 kürzlich 18 Min Lesezeit
0

LINQ and ZLinq in the Unity 6 Era: Avoiding GC Allocations in Large-Scale Projects

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




Introduction



In large-scale Unity development, GC Alloc can quietly become a real problem.



At first, nothing looks wrong. But as the project grows and you add more enemies, UI, master data, events, states, notifications, logs, and other systems, small allocations that happen every frame begin to pile up.



LINQ is especially convenient.




CODE
var aliveEnemies = enemies
.Where(x => x.IsAlive)
.OrderBy(x => x.DistanceToPlayer)
.ToList();






It is readable.

But if this kind of code runs every frame, it can become a source of both GC Alloc and CPU overhead.



Unity's official documentation also recommends reducing frequent managed heap allocations as much as possible, ideally getting close to 0 bytes per frame.







The short version



The point of this article is not to ban LINQ completely.



Do not use LINQ in hot paths just because it is readable.



Do not assume ZLinq solves everything just because you introduced it.



Those are the two main ideas.



A rough guideline looks like this:




































Area Guideline
Editor extensions, build scripts, debug code Regular LINQ is usually fine
Startup, loading, initialization LINQ can be fine, but measure when data size is large
Update / LateUpdate / FixedUpdate Avoid LINQ by default
Code that is not per-frame but still called frequently Consider ZLinq
Code that materializes results into List or Array Prefer reusing a preallocated List over LINQ/ZLinq
Burst / Job / NativeArray-heavy code This article does not go deep into this area; prefer for loops and Native Collections




Unity 6 GC assumptions



In Unity 6, Incremental GC is enabled by default.

Unity uses the Boehm-Demers-Weiser garbage collector. With Incremental GC, garbage collection work is split across multiple frames to reduce GC spikes.





You also need to be careful about platform-specific differences.

Unity 6.0's Web platform documentation explains that GC on Web platforms does not run incrementally over multiple frames like it does on other platforms. Instead, it runs once at the end of every frame.





The basic form looks like this:




CODE
using ZLinq;

private void UpdateEnemies(List<Enemy> enemies)
{
foreach (Enemy enemy in enemies
.AsValueEnumerable()
.Where(static x => x.IsAlive))
{
enemy.Tick();
}
}






Compared with regular LINQ, AsValueEnumerable() is added.




CODE
// Regular LINQ
foreach (Enemy enemy in enemies.Where(static x => x.IsAlive))
{
enemy.Tick();
}

// ZLinq
foreach (Enemy enemy in enemies.AsValueEnumerable().Where(static x => x.IsAlive))
{
enemy.Tick();
}






The code looks similar, but the enumeration mechanism is different.



However, the important point is that adding AsValueEnumerable() does not magically make everything zero-allocation.

If you call ToList(), a List is still created. If a lambda captures an external variable, allocations related to closures can still occur.





Where ZLinq is easy to use



ZLinq works well in cases like this:




CODE
foreach (Enemy enemy in _enemies
.AsValueEnumerable()
.Where(static x => x.IsAlive)
.Where(static x => x.IsVisible))
{
enemy.UpdateMarker();
}






This kind of code is a good fit when you want to:




  • Filter

  • Transform

  • Enumerate

  • Use the result immediately

  • Avoid ToList()



In large projects, API boundaries often use types such as IEnumerable<T> or IReadOnlyList<T>.

Abstract types give callers more flexibility, but in hot paths, the fact that the static type is abstract can make profiling and optimization harder.




CODE
private void ApplyDamage(IEnumerable<Enemy> enemies)
{
foreach (Enemy enemy in enemies)
{
enemy.Damage(10);
}
}






Enumeration through an abstract type like this can cause extra allocations depending on the implementation and how the code is written.



ZLinq explains that even when AsValueEnumerable() is used on IEnumerable<T>, it may be able to reduce allocations if the actual object is an array or List<T>.





ZLinq also provides APIs such as CopyTo(List<T>) for copying results into an existing destination.

CopyTo(List<T>) clears the destination List before filling it, so be careful if you want to preserve the existing contents.





When no capture is needed, use a static lambda.




CODE
foreach (Enemy enemy in _enemies
.AsValueEnumerable()
.Where(static x => x.IsAlive))
{
enemy.Tick();
}






Adding static prevents the lambda from capturing variables outside itself.




CODE
float range = 10;

// This captures range, so it cannot be static.
// .Where(static x => x.DistanceToPlayer < range)






In hot paths, either shape the code so that static lambdas can be used, or just write a for loop.






Be careful with Drop-in Generator



ZLinq also has a Drop-in Generator that redirects regular LINQ calls toward ZLinq.



It is convenient, but in large projects, applying it broadly from the beginning is risky.



The ZLinq README explains that Drop-in Generator uses a Source Generator to generate extension methods for each type, and that these extension methods take priority over regular LINQ.

It also explains that, when using Drop-in Generator in Unity, the minimum Unity version is 2022.3.12f1 because of C# Incremental Source Generator support.





Since this article assumes Unity 6.0, the version requirement is usually not a problem. But if you apply it to the whole project at once, the following issues can happen:




  • It becomes harder to understand the difference from regular LINQ

  • It becomes harder to tell where ZLinq is being used

  • It spreads before the whole team understands the behavior

  • API boundaries that take IEnumerable<T> become harder to reason about

  • The impact of dependency updates becomes broader



At first, I prefer writing AsValueEnumerable() explicitly.

That way, code review makes it clear that the code is intentionally using ZLinq.



When installing ZLinq through tools such as NuGetForUnity, it is safer to pin the versions of ZLinq itself and Drop-in Generator.

In team development, if dependency versions differ between developers, the assumptions used during review and benchmarking can also differ.



If you use Drop-in Generator, asmdef and namespace boundaries should also be part of the team rules.

Make it clear in which assemblies ZLinq should take priority over regular LINQ, and avoid accidentally applying it to third-party code or shared libraries.

Some places may also change return types or available APIs compared with System.Linq, so keep migration PRs small and check both profiler results and code diffs step by step.





Installing ZLinq in Unity



When using ZLinq in Unity, the README describes a flow where the main ZLinq package is installed through NuGetForUnity or a similar tool, while Unity-specific features are referenced through the ZLinq.Unity package via a Git URL.

The Unity package adds support for GameObject and Transform hierarchy traversal, among other things.





However, in truly low-level hot paths that use Jobs or Burst, data layout, Native Collections, and Burst compilation constraints often matter more than preserving a LINQ-like style.



So this article focuses on how to handle LINQ and ZLinq in ordinary Unity runtime code, rather than on whether ZLinq can be used with NativeArray.





Do not expect too much from SIMD in Unity



ZLinq has SIMD-related features, but the README explains that SIMD is not used in Unity because Unity references .NET Standard 2.1.





So when introducing ZLinq, it is safer to first apply it explicitly to the code that actually has a problem, instead of immediately applying Drop-in Generator to the whole project.

Check the following:




  • GC Alloc in the Editor

  • Profiler results in a Development Build

  • IL2CPP build time

  • Player size

  • Runtime performance on the target platform



ZLinq is useful, but the wider the introduction scope becomes, the more you need to check its impact on the build output.





Minimum profiling conditions



This article repeatedly says "check it with the Profiler", but it is important not to decide based only on Editor results.

Especially in production titles that use IL2CPP, GC Alloc and CPU time in the Mono Editor are not enough for the final decision.



At minimum, separate the following checks:




  • Use the Editor Profiler first to find where GC Alloc occurs

  • Check the target platform Player with a Development Build

  • If using IL2CPP, check CPU time, GC Alloc, Player size, and build time in an IL2CPP build

  • Deep Profile is useful, but it has large overhead, so do not use it blindly for final numeric judgment

  • Compare regular LINQ, ZLinq, and for loops with the same data size and call count

  • When introducing Drop-in Generator, compare profiler results, Player size, and build time before and after the change, ideally per PR



Also, even when GC Alloc becomes 0, CPU cost does not disappear.

Sorting with OrderBy, Transform hierarchy traversal, distance calculation, virtual calls, cache misses, and other costs remain.

ZLinq is a powerful option for reducing GC Alloc, but in hot paths, CPU time must be checked as well.





Suggested team rules



In large-scale development, rules matter more than individual discipline.



For example, the following rules are easy to review.





Places where LINQ is acceptable




  • Editor extensions

  • Importers

  • Build scripts

  • Debug code

  • Test code

  • One-time conversion at startup

  • Temporary processing during loading





Runtime areas that require confirmation




  • Update

  • LateUpdate

  • FixedUpdate

  • Per-frame UI updates

  • Updates for many enemies, bullets, effects, and similar objects

  • Scroll list rebuilding

  • Input handling

  • Camera control

  • State machines that run every frame



If LINQ or ZLinq is used in these areas, the author should be able to explain during review why it is acceptable.





Places where ZLinq is worth considering




  • Code that is not per-frame but is still called frequently

  • Code where filtering or transformation should remain readable

  • Code that enumerates results immediately without materializing a List

  • Existing codebases full of LINQ that need gradual GC reduction

  • Search code shared between Editor and Runtime





Places where for loops should be preferred




  • Truly hot code

  • Code called thousands of times per frame

  • Code that needs early break

  • Code that only needs the minimum, maximum, or first matching item

  • Code that reuses result Lists

  • Code strongly tied to Job / Burst / Native Collections





Code review checklist



In code review, it is more practical to ask the following questions than to reject every use of LINQ mechanically.




CODE
var result = source.Where(...).Select(...).ToList();






When you see code like this, check:




  1. Is it called every frame?

  2. Is the number of items large?

  3. Is ToList() or ToArray() really necessary?

  4. Is a closure being created?

  5. Is OrderBy really necessary?

  6. Is it sorting only to call FirstOrDefault?

  7. Can the result List be reused?

  8. Can ZLinq preserve readability while reducing allocations?

  9. Would a for loop be clearer?

  10. Is the ownership and lifetime of a reused List clear?

  11. Do we need to check the impact on IL2CPP builds?



OrderBy().FirstOrDefault() deserves particular attention.




CODE
var target = enemies
.Where(static x => x.IsAlive)
.OrderBy(static x => x.DistanceToPlayer)
.FirstOrDefault();






If you only need the minimum value, you do not need to sort.




CODE
Enemy target = null;
float minDistance = float.MaxValue;

for (int i = 0; i < enemies.Count; i++)
{
Enemy enemy = enemies[i];

if (!enemy.IsAlive)
{
continue;
}

float distance = enemy.DistanceToPlayer;
if (distance < minDistance)
{
target = enemy;
minDistance = distance;
}
}






In this kind of case, changing the algorithm is more effective than replacing LINQ with ZLinq.






Summary



LINQ and ZLinq are both useful.

But in large-scale Unity development, if you do not decide where they are allowed, convenient code can directly turn into runtime performance problems.



The policy in this article is simple:




  • Avoid LINQ in hot paths

  • Consider ZLinq for frequently called code where readability still matters

  • Be suspicious of ToList(), ToArray(), closures, and unnecessary OrderBy

  • Keep the ZLinq introduction scope controlled, and verify with the Profiler and IL2CPP builds



ZLinq is not a free pass to use LINQ carelessly.



It is best treated as:




An option for keeping LINQ readability while still thinking seriously about GC


Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten LINQ and ZLinq in the Unity 6 Era: Avoiding GC Allocations in Large-Scale Projects

Thematisch verwandte Begriffe: LINQ, ZLinq, Unity, Avoiding · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...