확인 문제
using System;
public class Logger
{
public string LogMessages { get; private set; }
public Logger()
{
LogMessages = string.Empty;
}
public void Log(string message)
{
LogMessages += message + "\n";
}
}
public class Program
{
public static void Main()
{
Logger logger = new Logger();
for (int i = 0; i < 10000; i++)
{
logger.Log("This is log message number " + i);
}
Console.WriteLine("Logging completed. Total log length: " + logger.LogMessages.Length);
}
}
1. 위의 코드가 문제가 되는 이유를 메모리 관점에서 설명해주세요.
`LogMessages`에 로그 메세지를 추가할 때마다 새로운 문자열 객체가 생성됩니다.
기존 문자열에 새 문자열을 연결하면 새로운 문자열 객체가 만들어지고, 기존의 문자열과 새 문자열이 복사됩니다.
이를 반복할수록 메모리 사용양이 기하급수적으로 증가하게 됩니다.
문자열을 반복해서 연결하는 과정에서 많은 메모리 할당 및 해제가 발생하여 메모리 단편화가 발생하기 때문입니다.
2. 아래와 같이 string이 아닌 StringBuilder가 권장되는 이유는 무엇일까요?
public class Logger
{
private StringBuilder logMessages;
public Logger()
{
logMessages = new StringBuilder();
}
public void Log(string message)
{
logMessages.Append(message).Append("\n");
}
public override string ToString()
{
return logMessages.ToString();
}
}
C#에서 `string`은 불변(immutable) 객체입니다. 즉, 문자열을 수정할 수 없으며, 수정 시마다 새로운 문자열 객체가 생성됩니다. 1번의 답변처럼 메모리 사용량을 크게 증가시키고, 성능을 저하시키게 됩니다.
반면 `StringBuilder`는 가변(mutable) 객체로, 문자열을 효율적으로 조작할 수 있습니다.
내부적으로 가변 길이의 배열을 사용하여 문자열을 저장하고, 필요할 때만 메모리를 확장합니다.
문자열을 연결할 때마다 새로운 객체를 생성하지 않으므로 메모리 단편화가 줄어듭니다.
설명 문제
1. 가비지 컬렉터란 무엇인가요?
가비지 컬렉터는 메모리 관리를 자동화하는 시스템입니다.
가비지 컬렉터의 주 목적은 사용되지 않는 객체들을 자동으로 식별하고 해제하여 메모리 누수를 방지하고, 메모리 효율성을 유지하는 것입니다. 이는 프로그래머가 수동으로 메모리를 관리해야 하는 부담을 줄여줍니다.
2. 가비지 컬렉터의 장점과 단점에 대해 설명해주세요.
장점은 프로그래머가 수동으로 메모리를 해제할 필요가 없으므로 코드의 안정성과 생산성이 높아집니다. 또한, 가비지 컬렉터가 더 이상 사용되지 않는 객체를 자동으로 해제하여 메모리 누수를 방지합니다.
단점은 가비지 컬렉션은 추가적인 CPU와 메모리 자원을 사용하기 때문에 성능 오버헤드가 발생할 수 있습니다.
3. 가비지 컬렉터의 세대 개념에 대해 설명해주세요.
가비지 컬렉터의 세대 개념은 객체의 생애 주기와 관련된 가설을 기반으로, 메모리 관리를 최적화하기 위해 고안된 기술입니다. 이를 세대별 가비지 컬렉션이라고 하며, 객체를 여러 세대로 분류하여 관리합니다.
4. 박싱, 언박싱을 사용할 때 주의해야 할 점은 무엇일까요?
박싱은 기본 타입 값을 힙(Heap)에 새로운 객체로 할당하므로 메모리 할당과 GC(가비지 컬렉션) 오버헤드가 발생할 수 있습니다. 특히 반복문 내에서 빈번하게 박싱을 수행하면 성능 저하를 일으킵니다.
언박싱은 언박싱을 시도할 때 객체가 `null`이면 `NullPointerException`이 발생합니다. 따라서 언박싱 전에 객체가 `null`인지 확인해야 합니다.
5. 오브젝트 풀을 사용하면 메모리 관리에 도움이 되는 이유가 무엇일까요?
객체 생성에는 메모리 할당, 초기화 등의 비용이 듭니다. 특히, 객체 생성이 빈번하게 이루어지는 경우 성능 저하가 일어납니다. 오브젝트 풀을 사용하면 객체를 미리 생성해 두고 재사용하므로, 새로운 객체를 생성하는 비용을 절감할 수 있습니다.
객체를 반복적으로 생성하고 소멸시키면 가비지 컬렉션이 자주 발생하게 되어 시스템 성능에 부정적인 영향을 미칠 수 있습니다. 오브젝트 풀을 사용하면 객체가 재사용되므로 가비지 컬렉션에 의해 수집되는 객체 수가 줄어들고, 이로 인해 GC의 빈도와 부담이 감소합니다.
실습 문제
💡 [EnemyPool 구현]
오브젝트 풀을 이용하여 적을 생성하는 EnemyPool을 구현해봅시다.
GetEnemy 메서드에서 재사용 가능한 Enemy 객체를 가져오고 초기화하는 코드를 작성하세요.
ReleaseEnemy 메서드에서 사용한 Enemy 객체를 반환하고, 재사용 리스트에 추가하는 코드를 작성하세요.
제공 코드▼
using System;
using System.Collections.Generic;
public class Enemy
{
public int HP { get; set; }
public int Attack { get; set; }
public Enemy(int hp, int attack)
{
HP = hp;
Attack = attack;
}
public void Reset(int hp, int attack)
{
HP = hp;
Attack = attack;
}
}
public class EnemyPool
{
private List<Enemy> availableEnemies = new List<Enemy>();
private List<Enemy> usedEnemies = new List<Enemy>();
public Enemy GetEnemy(int hp, int attack)
{
Enemy enemy;
if (availableEnemies.Count > 0)
{
// TODO : 재사용 가능한 Enemy 객체를 가져오고 초기화
//
}
else
{
enemy = new Enemy(hp, attack);
}
usedEnemies.Add(enemy);
return enemy;
}
public void ReleaseEnemy(Enemy enemy)
{
// TODO : 사용한 Enemy 객체를 반환하고, 재사용 리스트에 추가
//
}
}
public class Program
{
public static void Main()
{
EnemyPool enemyPool = new EnemyPool();
List<Enemy> currentEnemies = new List<Enemy>();
long beforeGcCount = GC.CollectionCount(0);
for (int frame = 0; frame < 10000; frame++)
{
// 일정 프레임마다 새로운 적 객체 생성
if (frame % 10 == 0) // 10프레임마다 적 생성
{
Enemy newEnemy = enemyPool.GetEnemy(100, 10);
currentEnemies.Add(newEnemy);
}
// 일정 조건에서 적 객체 반환
if (frame % 20 == 0 && currentEnemies.Count > 0) // 20프레임마다 적 반환
{
Enemy oldEnemy = currentEnemies[0];
currentEnemies.RemoveAt(0);
enemyPool.ReleaseEnemy(oldEnemy);
}
}
long afterGcCount = GC.CollectionCount(0);
Console.WriteLine("Total enemies created: " + (enemyPool.usedEnemies.Count + enemyPool.availableEnemies.Count));
Console.WriteLine("GC collection count before: " + beforeGcCount);
Console.WriteLine("GC collection count after: " + afterGcCount);
Console.WriteLine("GC collections occurred: " + (afterGcCount - beforeGcCount));
}
}
작성한 코드▼
public class Enemy
{
public int HP { get; set; }
public int Attack { get; set; }
public Enemy(int hp, int attack)
{
HP = hp;
Attack = attack;
}
public void Reset(int hp, int attack)
{
HP = hp;
Attack = attack;
}
}
public class EnemyPool
{
public List<Enemy> availableEnemies = new List<Enemy>();
public List<Enemy> usedEnemies = new List<Enemy>();
public Enemy GetEnemy(int hp, int attack)
{
Enemy enemy;
if (availableEnemies.Count > 0)
{
// TODO : 재사용 가능한 Enemy 객체를 가져오고 초기화
enemy = availableEnemies[0];
availableEnemies.RemoveAt(0);
enemy.Reset(hp, attack);
//
}
else
{
enemy = new Enemy(hp, attack);
}
usedEnemies.Add(enemy);
return enemy;
}
public void ReleaseEnemy(Enemy enemy)
{
// TODO : 사용한 Enemy 객체를 반환하고, 재사용 리스트에 추가
if (usedEnemies.Remove(enemy))
{
availableEnemies.Add(enemy);
}
//
}
}
public class Program
{
public static void Main()
{
EnemyPool enemyPool = new EnemyPool();
List<Enemy> currentEnemies = new List<Enemy>();
long beforeGcCount = GC.CollectionCount(0);
for (int frame = 0; frame < 10000; frame++)
{
// 일정 프레임마다 새로운 적 객체 생성
if (frame % 10 == 0) // 10프레임마다 적 생성
{
Enemy newEnemy = enemyPool.GetEnemy(100, 10);
currentEnemies.Add(newEnemy);
}
// 일정 조건에서 적 객체 반환
if (frame % 20 == 0 && currentEnemies.Count > 0) // 20프레임마다 적 반환
{
Enemy oldEnemy = currentEnemies[0];
currentEnemies.RemoveAt(0);
enemyPool.ReleaseEnemy(oldEnemy);
}
}
long afterGcCount = GC.CollectionCount(0);
Console.WriteLine("Total enemies created: " + (enemyPool.usedEnemies.Count + enemyPool.availableEnemies.Count));
Console.WriteLine("GC collection count before: " + beforeGcCount);
Console.WriteLine("GC collection count after: " + afterGcCount);
Console.WriteLine("GC collections occurred: " + (afterGcCount - beforeGcCount));
}
}
'스파르타 게임개발종합반(Unity) > 기술 면접 대비 꾸준 실습' 카테고리의 다른 글
07. C# 심화 문법 (0) | 2024.07.11 |
---|---|
05. 상속과 인터페이스 (0) | 2024.07.08 |
04. 스택 메모리 vs 힙 메모리 (0) | 2024.07.05 |
03. 콜백, delegate, event (1) | 2024.07.04 |
02. 객체지향 프로그래밍 (0) | 2024.07.03 |
댓글