Normalerweise will jeder Task seine Segmente dynamisch vergrößern, für Heap und so. Irgendwann kommen sich Segmente dadurch zwangsläufig in die Quere und dann beginnt der Spaß, da muss man die im Speicher hin und her schieben, dabei immer alle Datei mitkopieren, die Deskriptortabellen anpassen (sei es GDT, sei es LDT)…
Das Problem hat man mit Paging halt nicht, weil lineare Adressen nichts mit den physischen¹ zu tun haben. Die virtuellen Adressen der Segmentierung hingegen haben durchaus was mit den linearen² zu tun, weil sie kontinuierlich abbilden, von einer Basisadresse eine bestimmte Anzahl Bytes. Deshalb kann da überhaupt irgendwas kollidieren.
Das heißt auch, wenn man einem Programm ein Segment zuweist, dann muss der dahinter liegende physische Speicher auch tatsächlich existieren. Das ist bei Paging natürlich ganz anders, da muss vom gesamten Adressraum im Prinzip gar nichts physisch da sein (weil der Adressraum ja aus vielen einzelnen Segmenten (den Pages) besteht, die jeweils entweder da sind oder nicht). Man kann das gleiche Prinzip bestimmt auch bei Segmentierung anwenden, nur heißt das dann natürlich, dass jedes Programm ganz viele Segmente bekommt (bspw. für jedes Objekt eins). Und da hat man dann viel Spaß, einen Compiler zu finden, der so ein Speichermodell unterstützt.
tl;dr: Mit Segmentierung hat man nur Ärger, weil man entweder nicht mal eben irgendeinem Programm mehr Speicher zuweisen kann (bestimmt gibts noch andere Gründe), oder zumindest keinen Compiler findet, der sie unterstützt, wenn man sie ordentlich machen will.
¹Aus einer virtuellen Adresse wird mit Segmentierung eine lineare Adresse und daraus mit Paging eine physische. „Ohne“ Segmentierung sind also alle linearen Adressen äquivalent zu den virtuellen.
²Ohne Paging sind alle linearen Adressen äquivalent zu den physischen.